webpack2配置

详细的配置可以参考官网:https://doc.webpack-china.org/guides/

一开始做项目时都是直接从组里前辈搭建好的脚手架开始写代码,到后来自己写新项目时又是拷贝之前的工程作为脚手架开始。对于脚手架本身却不甚了解,不仅不思考为什么更是没有改进的想法,怪不得工作满一年了却总觉得自己的技术水平在原地踏步,就是没有总结和思考。

目前组里的技术栈都是使用vue+koa,使用webpack的好处一是方便写vue的单文件组件,二是打包文件方便生产部署再加上能无所顾虑的应用语言的新特性。

1. 配置文件

官方文档推荐写webpack配置文件时,先写出一个基本配置文件(base)包含入口、输出等,再根据开发/生产环境所需要插件的不同,利用webpack-merge生成三个配置文件: dev、prod、analyze

package.json 依赖参考

"devDependencies": {
    "babel-core": "^6.18.2",
    "babel-loader": "^7.1.1",
    "babel-preset-es2015": "^6.18.0",
    "css-loader": "^0.28.4",
    "koa-webpack-dev-middleware": "^1.4.0",
    "koa-webpack-hot-middleware": "^1.0.3",
    "less": "^2.7.1",
    "less-loader": "^4.0.5",
    "style-loader": "^0.18.2",
    "url-loader": "^0.5.7",
    "vue-loader": "^12.2.2",
    "webpack": "^3.3.0",
    "webpack-bundle-analyzer": "^2.8.3",
    "webpack-dev-middleware": "^1.11.0",
    "webpack-dev-server": "^2.6.1",
    "webpack-hot-middleware": "^2.13.1",
    "webpack-koa-hot-middleware": "^0.1.2",
    "webpack-manifest-plugin": "^1.2.1",
    "webpack-merge": "^4.1.0"
  }

 

webpack.config.base.js

 1 'use strict'
 2 let path = require('path');
 3 let webpack = require('webpack');
 4 let WebpackManifestPlugin = require("webpack-manifest-plugin");
 5 
 6 module.exports = {
 7     output: {
 8         path: path.resolve(__dirname, '..', 'build')
 9     },
10     resolve: {
11         extensions: ['.js', '.vue'],
12         modules: ['node_modules'],
13         alias: {
14             'leafletCSS': 'leaflet/dist/leaflet.css',
15             'leaflet$': 'leaflet/dist/leaflet.js',
16             'vue$': 'vue/dist/vue.min.js',
17             'vue-resource$': 'vue-resource/dist/vue-resource.min.js'
18         }
19     },
20     module: {
21         rules: [
22             {
23                 test: /\.vue$/,
24                 loader: 'vue-loader',
25             },
26             {
27                 test: /\.js$/,
28                 exclude: /(node_modules|bower_components)/,
29                 use: {
30                     loader: 'babel-loader',
31                     options: {
32                         presets: ['es2015']
33                     }
34                 }
35             },
36             {
37                 test: /\.css$/,
38                 use: [
39                     'style-loader',
40                     'css-loader'
41                 ]
42             },
43             {
44                 test: /\.(png|jpe?g|gif|svg|woff2?|ttf|otf)(\?.*)?$/,
45                 loader: 'url-loader',
46             },
47             {
48                 test: /\.less$/,
49                 use: [
50                     'style-loader',
51                     'css-loader',
52                     'less-loader'
53                 ]
54             }
55         ]
56     },
57     plugins: [
58         new WebpackManifestPlugin(),
59         new webpack.optimize.CommonsChunkPlugin({
60             name: 'vendor',
61             minChunks: function (module) {
62                 // this assumes your vendor imports exist in the node_modules directory
63                 return module.context && module.context.indexOf('node_modules') !== -1;
64             }
65         }),
66         new webpack.optimize.CommonsChunkPlugin({
67             name: 'common',
68             chunks: ['qq', 'navi', 'log', 'guide', 'apply', 'voice', 'pianhang', 'dynamic'], //这里输入需要提取公共代码的entry
69             minChunks: 2
70         }),
71         //CommonChunksPlugin will now extract all the common modules from vendor and main bundles
72         new webpack.optimize.CommonsChunkPlugin({
73             name: 'manifest', //But since there are no more common modules between them we end up with just the runtime code included in the manifest file
74         }),
75     ]
76 }

第16行代码可以参考这里这里,默认NPM包导出的是运行时构建,Vue2的运行时构建不支持单文件组件的template

第58行的 WebpackManifestPlugin 作用是将输出文件名保存在文件中 (当输出文件名带 chunkhash 时很有用,参考这里)

第59~74行的 CommonsChunkPlugin 作用是从打包后的 bundle 文件中提取公共模块,将 npm install 的公共模块和业务代码分开,这样浏览器就可以一直缓存公共模块的bundle,参考这里。第一个 CommonsChunkPlugin 作用是将 node_modules 里的模块提取到 vendor.js 里;第二个 CommonsChunkPlugin 作用是将 entry 里的公共代码提取出来放在 common.js 里,参考这里;第三个 CommonsChunkPlugin 作用是将 webpack 运行时代码放在 manifest.js 里

 

webpack.config.dev.js

 1 "use strict"
 2 let webpack = require('webpack');
 3 let merge = require('webpack-merge');
 4 let base_config = require('./webpack.config.base');
 5 
 6 module.exports = merge(base_config, {
 7     entry: {
 8         qq: ['./src/qq/qq.js', 'webpack-hot-middleware/client'],
 9         navi: ['./src/navi/navi.js', 'webpack-hot-middleware/client'],
10         log: ['./src/log/log.js', 'webpack-hot-middleware/client'],
11         guide: ['./src/guide/guide.js', 'webpack-hot-middleware/client'],
12         apply: ['./src/apply/apply.js', 'webpack-hot-middleware/client'],
13         voice: ['./src/voice/voice.js', 'webpack-hot-middleware/client'],
14         pianhang: ['./src/pianhang/pianhang.js', 'webpack-hot-middleware/client'],
15         dynamic: ['./src/dynamic/dynamic.js', 'webpack-hot-middleware/client']
16     },
17     output: {
18         filename: '[name].js',
19     },
20     devtool: '#eval-source-map',
21     plugins: [
22         new webpack.HotModuleReplacementPlugin(),
23     ]
24 });

第 20 行设置source-map,方便用浏览器查看源代码

 

webpack.config.prod.js

 1 "use strict"
 2 let webpack = require('webpack');
 3 let merge = require('webpack-merge');
 4 let base_config = require('./webpack.config.base');
 5 
 6 module.exports = merge(base_config, {
 7     entry: {
 8         qq: ['./src/qq/qq.js'],
 9         navi: ['./src/navi/navi.js'],
10         log: ['./src/log/log.js'],
11         guide: ['./src/guide/guide.js'],
12         apply: ['./src/apply/apply.js'],
13         voice: ['./src/voice/voice.js'],
14         pianhang: ['./src/pianhang/pianhang.js'],
15         dynamic: ['./src/dynamic/dynamic.js'],
16     },
17     output: {
18         filename: '[name].[chunkhash].js',
19     },
20     plugins: [
21         new webpack.optimize.UglifyJsPlugin()
22     ]
23 });

第 21 行 UglifyJsPlugin 的作用是压缩、混淆代码

 

webpack.config.analyze.js

 1 "use strict"
 2 
 3 let webpack = require('webpack');
 4 let merge = require('webpack-merge');
 5 var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
 6 let prod_config = require('./webpack.config.prod');
 7 
 8 module.exports = merge(prod_config, {
 9     plugins: [
10         new BundleAnalyzerPlugin()
11     ]
12 });

analyze 文件的作用利用 webpack-bundle-analyzer 插件分析打包情况,

webpack --config ./config/webpack.config.analyze.js

运行结果如下:

 

webpack的配置文件自定义程度很高,所以在参考他人配置时最好能弄清楚为什么要这样写

 

2. webpack-dev-server 

在开发过程中另一个重要的东西是 webpack-dev-server,它的作用是当你改动源代码后能自动重新打包,再加上 webpack 的HMR-模块热替换特性,这样改动代码就能直接在浏览器里看到效果,省却了代码手动打包+刷新浏览器的步骤。使用 webpack-dev-server 有 CLI 和 API 两种使用方法。

CLI 方式设置 dev 文件中的 HotModuleReplacementPlugin 和 devServer 启用 HMR。启动的命令为:

webpack-dev-server --config ./config/webpack.config.dev.js

API 方式直接在命令里设置参数,如:(这里用到了 Unix Domin Socket,也可以直接指定 ip和端口)

webpack-dev-server --config config/webpack.dev.config.js --public 0.0.0.0:8056 --progress --inline --hot --socket .dev-shared/sockets/webpack.sock

webpack-dev-server 的方式配置简单,缺点是引入 bundle 比较麻烦,需要指定其它端口

 

3. webpack-dev-middleware 

上述配置文件就是使用的该方法,需要有 koa-webpack-dev-middleware、koa-webpack-hot-middleware(热更新)。然后在 index.js 里写:

1 if (process.env.NODE_ENV == 'dev') {
2     let webpack = require('webpack');
3     let webpackConfig = require('./config/webpack.config.dev.js');
4     let webpackDevMiddleware = require('koa-webpack-dev-middleware');
5     let webpackHotMiddleware = require('koa-webpack-hot-middleware');
6     let compiler = webpack(webpackConfig);
7     app.use(webpackDevMiddleware(compiler));
8     app.use(webpackHotMiddleware(compiler));
9 }

这样就不需要通过额外的端口获取 bundle 文件了,注意这里是 koa 环境

 

4. 如何在前端框架里引入 bundle

由于 webpack prod 配置文件里使用了 chunkhash 作为 bundle 的名字的一部分,修改业务代码,chunkhash 会发生改变,所以需要通过一些方法自动将 bundle 名字注入到前端页面里:

第一种方法是通过在后端 controller 里读取 manifest.json 里的内容,然后通过模板引擎,注入到页面里,例如 nunjucks:

{% for path in paths %}
    <script src="{{path}}"></script>
{% endfor %}

第二种方法是拓展模板引擎命令,例如 xtpl:

{{{ xScript('manifest.js') }}}
{{{ xScript('vendor.js') }}}
{{{ xScript('common.js') }}}
{{{ xScript('apply.js') }}}

xtpl 命令拓展示例 xtpl.ext.js :

 1 let xtplApp = require('xtpl/lib/koa');
 2 let xtpl = require('xtpl/lib/xtpl');
 3 let XTemplate = xtpl.XTemplate;
 4 
 5 XTemplate.addCommand('xScript', function(scope, option){
 6     let name = option.params[0];
 7     if (process.env.NODE_ENV !== 'dev') {
 8         let assets_map = require('./manifest');
 9         name = assets_map[name];
10     }
11     return '<script src="' + name + '" ></script>';
12 });
13 
14 module.exports = xtplApp;

然后在 index.js 里

let xtpl = require('./extensions/xtpl.ext');

 

注意引入 bundle 的时候要注意引入顺序:manifest > vendor > common > entry,否则可能会报 ReferenceError: webpackJsonp is not defined 错误,还要注意要有 .babelrc 文件:

{
    "presets": ["es2015"]
}

否则会报 SyntaxError: Unexpected token: name (xxxxxx) from Uglify plugin 之类的错误,无法识别语言新特性

posted @ 2017-07-09 11:34  cqq626  阅读(854)  评论(0编辑  收藏  举报