webpack2学习总结

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/liujie19901217/article/details/71405081

webpack2学习总结

为什么使用 webpack?

webpack 是一款非常强大的前端资源处理工具,可以把所有前端需要的资源统一处理。比如js文件,css文件,甚至图片,字体文件,html文件。通过一个个独特的 loader 来对文件进行一些处理。

为什么升级 webpack2?

既然 webpack1 已经很好用了,为什么还要费大力气升级 webpack2,而且坑也比较多。
主要是因为两个原因:
1. 服务端渲染
如果哪位读者做过服务端渲染就会知道,路由的异步加载需要用到 require.ensure,但是这在服务端没有,所以需要 polyfill,很麻烦。而升级到 webpack2 的话就不需要处理这些,用自带的 import 即可。
2. 性能
webpack2 用了 Treeshaking。可以分析引用文件,进而不加载不需要的文件,有效减少文件的大小,减少一定的加载时间,提升用户体验。
就最后的结果来看,服务端渲染正常,而前端js代码文件明显减少,效果很好。

webpack功能

  1. 会将js代码打包到一个文件中,还会将css、图片以及其他资源打包到同一个包中(而其他打包工具只会对js脚本文件进行打包处理)
  2. 在打包之前对文件进行预处理(less/coffee/jsx等)
  3. 根据入口文件的不同把你的包拆分成多个包(可分离打包)
  4. 支持开发环境的特性标志位
  5. 支持模块代码热替换(代码修改后自动刷新浏览器)
  6. 支持异步加载

安装

全局安装

$ npm install webpack -g

项目中还是应该把依赖写入 package.json 中更人性化:

# 创建package.json文件
$ npm init
# 快速创建package.json文件
$ npm init -y
# 项目目录中安装webpack
$ npm install webpack --save-dev

相关配置文件

const path = require('path');
const webpack = require('webpack');
module.exports = {
  //__dirname是当前文件所在目录
  context: path.resolve(__dirname, './src'),
  entry: {
    app: './app.js',
  },
  output: {
    path: path.resolve(__dirname, './dist'),
    filename: '[name].bundle.js',
  },
};

Webpack 的工作流

Webpack 是如何知道项目如何运行的? 它是通过读取相关代码来获知这一信息的。
相关流程:

  1. 从 context 文件夹开始;
  2. 查找 entry 对应的文件;
  3. (找到文件之后)读取文件内容. 每当遇到 import (ES6) 或者 require() (Node) 依赖项时, 它会解析这些代码, 并且打包到最终构建里. 接着它会不断递归搜索实际需要的依赖项, 直到它到达了“树”的底部;
  4. 递归完所有依赖之后, Webpack 会将所有东西打包到 output.path 对应的目录, 并将 output.filename 的值作为最终的资源名([name] 表示使用 entry 项的 key)。

假设src/app.js 中的代码如下:(假设之前已运行了 npm install –save moment):

import moment from 'moment';
let rightNow = moment().format('MMMM Do YYYY, h:mm:ss a');
console.log(rightNow);
// "October 23rd 2016, 9:30:24 pm"

(在项目的根目录下)运行:

//-p 表示生产模式, 输出文件会被压缩
webpack -p

运行该命令之后, Webpack 会打包输出一个 dist/app.bundle.js 文件, 同时在控制台输出当前日期。
需要注意的是: Webpack 会自动找到 moment 的指向(即使你有一个 moment.js 存在于目录中, 但 Webpack 默认会优先去寻找 moment 的Node模块).

loaders

每引入一种loader,就相当于告诉了webpack:当遇到某种类型的文件时,就使用该loader来解析。
preLoaders顾名思义就是在loaders执行之前处理的,webpack的处理顺序是preLoaders - loaders - postLoaders

解析less文件

对于 less 文件,项目中使用了less,解析less文件需要使用3个loader:style-loader,css-loader,以及less-loader。
安装命令如下:

$ npm install style-loader css-loader less-loader less --save-dev

其中:

  • less-loader用来将less解析为css;
  • css-loader能够解析import以及url()的语法;
  • style-loader可以将生成的css插入到HTML文档中(注意最后只生成一个bundle.js)。
    相应的webpack.config.js中解析less的配置如下:
// options.module
module: {
    rules: [
        {
            test: '/\.less$/',
            use: ['style-loader', 'css-loader', 'less-loader']//从右至左依次解析
        }
    ]
}

解析jsx以及es6文件

因为webpack默认是可以解析commonJS,AMD以及es6的模块语法的,所以即使不使用任何的loader,import以及require语法也可以直接使用。
但是除了import/export这些模块语法,webpack是不会动你其他代码的。所以,如果我们在项目中使用es6、JSX等语法,还需要添加babel-loader
安装命令如下:

$ npm install babel-core babel-loader babel-preset-es2015 babel-preset-react --save-dev

webpack.config.js中解析jsx以及es6的配置如下:

module: {
    rules: [
    {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/, // 这里面的不解析
        use: {
          loader: 'babel-loader',
          options: { 
          presets: ['es2015']
          }
        }
    }
    ]
}

如果安装了babel-preset-latestbabel-preset-env, 意思是转换 ES2015 / ES2016 / ES2017 到ES5, 不只是转换ES2015。

$ npm install --save-dev babel-preset-env
或者
$ npm install --save-dev babel-preset-latest

# 需要将presets中改成env或者latest
presets: ['env']

也可以在 package.json 加入 babel 配置:

{
  "babel": {
    "presets": [
      "latest"
    ]
  }
}

打包时babel会读取package.jsonbabel 字段的内容, 然后执行相应的转换。

插件

  • CommonsChunkPlugin插件
    用于提取多个入口文件(多个页面之间)的公共脚本部分,然后生成一个 common.js 来方便多页面之间进行复用。

  • extract-text-webpack-plugin插件
    希望项目的样式能不要被打包到脚本中,单独提取出项目的样式文件

resolve 配置项

resolve: {
        //查找module的话从这里开始查找
        root: 'E:/github/flux-example/src', //绝对路径
        //自动扩展文件后缀名,意味着我们import模块可以省略不写后缀名
        //注意webpack2不在支持第一项为空字符串
        extensions: ['.js', '.json', '.scss'],
        //模块别名定义,方便后续直接引用别名,无须多写长长的地址
        alias: {
            AppStore : 'js/stores/AppStores.js',
            ActionType : 'js/actions/ActionType.js',
            AppAction : 'js/actions/AppAction.js'
        }
    }

webpack打包参数说明

# --config用来指定打包使用的配置文件,默认使用webpack.config.js
$ webpack --config XXX.js

# --watch 监听文件变动并自动打包
$ webpack --watch
或者
$ webpack -w

# --progress 编译的输出内容带有进度
$ webpack --progress

# --colors 编译的输出内容带有颜色
$ webpack --colors

# --open 在浏览器中自动打开网页
$ webpack --open

# -p 压缩混淆脚本
$ webpack -p

# -d 生成map映射文件,告知哪些模块被最终打包到哪里了
$ webpack -d 

# --display-error-details 打印错误详情
$ webpack --display-error-details

# 打包模块
$ webpack --display-modules  

# 打包原因
$ webpack --display-reasons 

部署上线

当项目已经开发完了,需要部署上线了。我们应该新创建一个单独的config文件,因为部署上线使用webpack的时候我们不需要一些dev-tools,dev-server和jshint校验等。
复制我们现有的config文件,命名为 webpack.production.config.js,将里面关于 devServer等和开发有关的东西删掉。
在package.json中添加一个 build 命令:

  "scripts": {
    "start": "webpack-dev-server --hot --inline",
    "build": "webpack --progress --profile --colors --config webpack.production.config.js"
  }

当要上线的时候,运行:

npm run build

可以发现build文件夹中生成了所有文件。
生产与开发需要不同的webpack.config.js :

  "scripts": {
    "start": "webpack --progress --profile --colors --config webpack.dev.config.js",
    "build": "webpack --progress --profile --colors --config webpack.pro.config.js"
  }

开发环境

在开发环境中, Webpack 可以提供一个开发服务器webpack-dev-server, 因为无论是你正在开发一个静态网站还是仅用于项目的前端原型设计, 它都能满足你的需要。为启动服务器, 仅需要在 webpack.config.js 中添加一个 devServer 对象:

module.exports = {
  context: path.resolve(__dirname, './src'),
  entry: {
    app: './app.js',
  },
  output: {
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, './dist/assets'),
    //publicPath是webpack-dev-server打包后输出文件所在的虚拟目录
    publicPath: '/assets',//New
  },
  devServer: {
    contentBase: path.resolve(__dirname, './src'),//New
  },
};

在 项目的根目录下创建一个带如下标签的 index.html 文件:

<script src="/assets/app.bundle.js"></script>

在终端运行如下命令:

webpack-dev-server

开发服务器会运行在 localhost:8080(打开你的浏览器访问该地址就能看到你的页面)。
需要注意的是: script 标签里的 /assets 是和 output.publicPath 匹配的。你可以把它命名成任何你想要的名字(如果你使用CDN, 这会很有用)。

Webpack 提供了热加载功能. 当你修改了 JavaScript 文件后, Webpack 会自动重新加载资源, 而不需要你手动去刷新浏览器。但是, 任何对 webpack.config.js 文件的改变都需要重启服务器才会生效。

webpack-dev-server(开发调试服务器)

这是Webpack 它自己的开发服务器,想要运行它,只需要在 webpack.config.js 里添加一个 devServer 对象。
webpack-dev-server是一个小型的Node.js Express服务器。
webpack-dev-server为项目提供了一个服务器,并重新加载。
可以webpack.config.js中配置,常用属性: compress,port,host, hot, inline
对于hot 属性官网解析为是否启动热更新,然而并没有什么用,打开浏览器会说 HMR不可用, 需要在命令行中使用 --hot 才行, $ webpack-dev-server –hot
热模块替换的好处是:只替换更新的部分,而不是页面重载。

 devServer: {
        //告诉服务器从哪里提供内容。这只有在您想要提供静态文件时才需要。例如图片
        contentBase: path.join(__dirname, 'dist'),
        // contentBase: false,
        //告诉服务器观看由devServer.contentBase选项提供的文件,文件更改将触发整个页面重新加载。
        watchContentBase: true,
        //随所有内容启用gzip压缩
        compress: true,
        port: 9997,
        host: '0.0.0.0',
        //这个是使用热更新的标志,然后并不提供热更新功能,需要引入hotModule
        // hot:true, 不加入HotModuleReplacementPlugin,因为API无法访问您的webpack配置
        //hot添加它。 (因为CLI可以访问您的webpack配置)
        hot: true,
        //在构建失败的情况下,启用热模块替换(请参阅devServer.hot)而不刷新页面作为回退。
        hotOnly: true,
        //devtool控制台显示信息
        clientLogLevel: 'none', //none, info, (warning,error 一直有)
        //延迟编译,对于异步模块,只有在请求时才会编译,在生产中不需要
        lazy: true,
        filename: "bundle.js",
        //为所有请求添加请求头
        headers: {
            "X-Custom-Foo": "bar"
        },
        //使用HTML5 History API时,系统可能会发送index.html网页来取代404回应
        historyApiFallback: true,
        https: true, //使用https协议
        //在开发服务器的两种不同模式之间切换(--inline, --iframe)。默认情况下,将使用内联模式启用应用程序。这意味着一个脚本将插入到您的包中以处理实时重新加载,并且构建消息将显示在浏览器控制台中。
        inline: true,
        //隐藏webpack打包时的信息
        noInfo: true,
        //使用代理,需要 http-proxy-middleware  代理包,连接后台接口的时候使用
        proxy: {
            "/api": "http://localhost:3000"
        },
        public: "myapp.test:80",
        //也是静态文件的目录, 相当于 output.publicPath
        publicPath: "/assets/",
        //启用安静功能后,除了初始启动信息之外的任何内容都将写入控制台。这也意味着来自webpack的错误或警告不可见。
        quiet: true
    }

devserver配置选项:

contentBase:默认webpack-dev-server会为根文件夹提供本地服务器,如果想为另外一个目录下的文件提供本地服务器,应该在这里设置其所在目录(本例设置到“public”目录)
port:设置默认监听端口,如果省略,默认为”8080inline:设置为true,当源文件改变时会自动刷新页面
colors:设置为true,使终端输出的文件为彩色的
historyApiFallback:在开发单页应用时非常有用,它依赖于HTML5 history API,如果设置为true,所有的跳转将指向index.html

webpack-dev-server优点

webpack-dev-server有利于在开发模式下编译。其是一个基于Express的web server,默认监听8080端口。server内部调用Webpack,这样做的好处是提供了额外的功能如热更新Live Reload以及热替换Hot Module Replacement(即HMR)。

$ webpack-dev-server --hot --inline

特别注意:该命令在每次修改文件,是将文件打包保存在内存中并没有写在磁盘里(默认是根据webpack.config.js该配置 文件打包文件,可以通过–config xxxx.js修改相应的打包配置文件)。这种打包得到的文件和项目根目录中的index.html位于同一级(但是你看不到,因为它在内存中并没有在磁盘里)。使用webpack命令将打包后的文件保存在磁盘中。
举例说明:
在index.html文件中引入通过webpack-dev-server --hot --inline打包的bundle.js

<script src="build.js"></script>

在index.html文件中引入通过webpack命令打包的bundle.js

<script src="./build/build.js"></script>

inline选项会为入口页面添加热加载功能,hot选项则开启热替换(Hot Module Replacement),即尝试重新加载组件改变的部分(而不是重新加载整个页面)。如果两个参数都传入,当资源改变时,webpack-dev-server将会先尝试HRM(即热替换),如果失败则重新加载整个入口页面。

# 当资源发生改变,以下三种方式都会生成新的bundle,但是又有区别:

# 当资源发生改变,不会刷新页面
$ webpack-dev-server
# 当资源发生改变,刷新页面
$ webpack-dev-server --inline
# 当资源发生改变,重新加载改变的部分,HRM失败则刷新页面
$ webpack-dev-server  --inline --hot

配置根目录

当在命令行中输入webpack-dev-server --hot --inline,再在浏览器中输入localhost:端口号,浏览器会在项目的根目录中去查找内容,通过--content-base可以配置根目录。
比如说:

webpack-dev-server --open --inline --progress --colors --hot --content-base ./build/
# --open 自动在浏览器中打开index.html页面
# --progress 显示打包进度
# --colors 打包说明带有颜色
# --hot 启动热模块更新

build文件夹中去加载index.html,如果没有
index.html文件,将会在浏览器中显示所有build目录下的文件和文件夹。

webpack-dev-server环境中path、publicPath、–content-base 区别与联系

path:指定编译目录而已(/build/js/),不能用于html中的js引用。
publicPath:虚拟目录,自动指向path编译目录(/assets/ => /build/js/)。html中引用js文件时,必须引用此虚拟路径(但实际上引用的是内存中的文件,既不是/build/js/也不是/assets/)。
 --content-base:必须指向应用根目录(即index.html所在目录),与上面两个配置项毫无关联。

发布至生产环境:
1. webpack进行编译(当然是编译到/build/js/2. 把编译目录(/build/js/)下的文件,全部复制到/assets/目录下(注意:不是去修改index.html中引用bundle.js的路径)

处理多个文件

多个(入口)文件一起打包

const path = require('path');
const webpack = require('webpack');
module.exports = {
  context: path.resolve(__dirname, './src'),
  entry: {
    app: ['./home.js', './events.js', './vendor.js'],
  },
  output: {
    path: path.resolve(__dirname, './dist'),
    filename: '[name].bundle.js',
  },
};

按照入口文件在数组中的顺序, 所有文件会被打包到dist/app.bundle.js
一个文件当中。

多个(入口)文件, 多个输出(文件)

const path = require('path');
const webpack = require('webpack');

module.exports = {
  context: path.resolve(__dirname, './src'),
  entry: {
    home: './home.js',
    events: './events.js',
    contact: './contact.js',
  },
  output: {
    path: path.resolve(__dirname, './dist'),
    filename: '[name].bundle.js',
  },
};

如果不想把所有打包在一个文件中, 你可以选择将多个文件打包在多个文件中. 上述例子会输出三个文件: dist/home.bundle.js, dist/events.bundle.js 和 dist/contact.bundle.js.

自动打包第三方库

如果你正在将应用拆解,打包成多个 output 的话(如果应用的某部分有大量不需要提前加载的 JS 的话,这样做会很有用),那么在这些文件里就有可能出现重复的代码,因为在解决依赖问题的时候它们是互相不干预的。幸好,Webpack 有一个内建插件 CommonsChunk 来处理这个问题:

module.exports = {
  // …
  plugins: [
    new webpack.optimize.CommonsChunkPlugin({
      name: 'commons',
      filename: 'commons.js',
      minChunks: 2,
    }),
  ],
// …
};

现在,在 output 的文件里,如果有任意模块加载了两次及以上(通过 minChunks 设置该值),它就会被打包进 commons.js文件中。
我们可以在客户端缓存这些模块. 虽然这会增加额外的请求, 但这能防止客户端多次下载同一个模块。所以在很多场景下,这都是提升速度的举措。

手工打包第三方库

如果你喜欢自己做更多的事情,您可以选择采用更人工的方法:

module.exports = {
  entry: {
    index: './index.js',
    vendor: ['react', 'react-dom', 'rxjs'],
  },
  // …
}

在这里,你明确告诉 webpack 导出包含 react, react-dom, 和 rxjs Node 模块的vendor 包,而不是依靠 CommonsChunkPlugin自动这些事情。

webpack打包生成多个vendor的配置方法

用webpack打包项目的时候,一般喜欢把一些公用的库打包的vendor.js里面,比如像react,react-router,redux等。
随着引入的库越来越多,vendor文件也变得越来越大,于是考虑打包成两个vendor,把和react相关的库打包成一个vendor,其他的库打包成另外一个vendor。按照webpack的文档开始配置,需要注意有两个比较坑的地方。
参考配置:

entry: { 
    //react相关库
    "vendor1": ["react", "react-dom", "react-router", "react-router-redux", "react-redux", "redux"],
    //其他库 
    "vendor2": ["crypto-js", "fastclick"],
    "app": "./js/index.js" 
},
plugins: [
    new webpack.optimize.CommonsChunkPlugin({
        names: ["vendor2","vendor1"],
        minChunks: Infinity
    })
]

第一个要注意的地方,在CommonsChunkPlugin里面,vender的顺序要反着来,总之,要和加载顺序相反。比如你想按vendor1,vendor2的顺序加载,entry里面写的是vendor1,vendor2,在CommonsChunkPlugin里面要写vendor2,vendor1。

第二个要注意的地方,output.filename一定不要写死了,要配置成可替换的,类似filename: '[name].js'形式。

缓存

为了能够使 webpack 处理后的静态资源能够长期缓存下来,需要:
使用 [chunkhash]给每一个文件创建基于内容变化的缓存标识

webpack的配置文件webpack.config.js

module.exports = {
    //入口文件
    entry:'./src/main.js',
    output:{
        //打包后文件路径
        path:'./dist',
        //打包后文件名
        filename:'bundle.js'
    }
}

猜你喜欢

转载自blog.csdn.net/liujie19901217/article/details/71405081