【转】从零搭建 vue2 vue-router2 webpack3 工程

以新手视角,详细介绍各个步骤内容,不深刻讲步骤涉及的原理,主要介绍如何操做。css

初始化工程

新建工程目录 vue2practice,在目录下执行npm init -y来建立一个 package.json,在 package.json 中先添加如下必备模块:html

{
  "name": "vue2-vue-router2-webpack3",
  "version": "1.0.0",
  "devDependencies": {
    "vue": "^2.4.2",
    "vue-loader": "^13.0.2",
    "vue-router": "^2.7.0",
    "vue-template-compiler": "^2.4.2",
    "webpack": "^3.4.1",
    "webpack-dev-server": "^2.6.1"
  }
}

 其中 vue-template-compiler 是 vue-loader 的 peerDependencies,npm3 不会自动安装 peerDependencies,然而 vue-template-compiler 又是必备的,那为何做者不将其放到 dependencies 中呢?有人在 github 上提过这个问题,我大体翻译一下做者的回答(仅供参考):这样作的缘由是由于没有可靠的方式来固定嵌套依赖的关系,怎么理解这句话?首先 vue-template-compiler 和 vue 的版本号是一致的(目前是同步更新),将 vue-template-compiler 指定为 vue-loader 的 dependencies 并不能保证 vue-template-compiler 和 vue 的版本号是相同的,让用户本身指定版本才能保证这一点。查看做者的回答(英文) 。若是二者版本不一致,运行时会出现下图所示的错误提示。vue

新建目录结构以下,新增的目录及文件先空着,后面的步骤会说明添加什么内容。node

vue2pratice
    |-- package.json
    |-- index.html         // 启动页面
    |-- webpack.config.js  // webpack配置文件
    |-- src
        |-- views       // vue页面组件目录
        |-- main.js     // 入口文件
        |-- router.js   // vue-router配置
        |-- app.vue     // 工程首页组件

配置Webpack

Webpack 默认读取 webpack.config.js,文件名不能随便改,其中 entry 是必须配置的。webpack

module.exports = {
    entry: './src/main.js',
    output: {
        path: __dirname + '/dist',
        publicPath: '/static/',
        filename: 'build.js'
    }
}

 Webpack 2+ 要求output.path必须为绝对路径。关于 Webpack 的各类路径在《详解Webpack2的那些路径》有详细的介绍git

配置 webpack-dev-server,只需在 package.json 添加如下启动命令便可。github

"scripts": {
  "dev": "webpack-dev-server --hot --open"
}

webpack-dev-server 2 默认为 inline 模式,热模块替换仍需本身设置。web

验证配置

在 index.html 中添加测试代码,引入打包后的 JS 文件。vue-router

<body>
    Hello, Webpack 3.
    <br>
    <script src="/static/build.js"></script>
</body>

在 main.js 中添加测试代码。vue-cli

// main.js
document.write('来自main.js的问候!')

执行下面的命令来安装模块并启动服务器。

// 安装依赖
npm install

// 运行
npm run dev

启动后浏览器会自动打开http://localhost:8080,若是控制台没有报错,页面正确显示 main.js 和 index.html 的内容,改动 main.js 后浏览器不刷新能看到效果,则表示配置没问题。

Vue

新建页面

在 views 目录下新建 index.vue。

<template>
    <div>
        这是{{page}}页面
    </div>
</template>
<script>
export default {
    data: function () {
        return {
            page: 'index'
        }
    }
}
</script>

webpack 1 须要特定的 loader 来转换 ES 2015 import/export,webpack 2 起能够开箱即用。可是 ES6 的新语法仍是须要 loader 来转换,在没有配置前,先不要用新语法。用了也没报错(好比 let,const等),那是由于你的浏览器已经支持了 ES6 语法(新版本浏览器都已经支持)。

配置路由

将 vue-router 实例化传入的参数new VueRouter(参数)提取到 router.js 造成路由配置文件。

import index from './views/index.vue'
export default {
    routes: [
        {
            path: '/index',
            component: index
        }
    ]
}

从 vue-loader@13.0.0,不能用 require 来引入 .vue 文件,由于 .vue 文件最终会被编译成 ES6 module。

 

首页

首页引入 ouput 配置的 JS,添加 Vue 实例的挂载目标。

<body>
<div id="app"></div>
<script src="/static/build.js"></script>
</body>

入口JS完成路由配置、初始化 Vue 实例。

import Vue from 'vue';
import VueRouter from 'vue-router';
import App from './app.vue';
import routerConfig from './router';
Vue.use(VueRouter);
var router = new VueRouter(routerConfig)
new Vue({
    el: '#app',
    router: router,
    render: h => h(App)
});

从 Vue 2.2.0 后使用 require('vue') 会报错,应使用 ES6 module(import),具体缘由请参考 Vue 更新说明 https://github.com/vuejs/vue/releases,截图以下:

在首页组件 app.vue 中添加路由连接、路由视图组件。

<template>
    <div>
        <div>
            <router-link to="/index">Home</router-link>
        </div>
        <div>
            <router-view></router-view>
        </div>
    </div>
</template>

配置loader

配置 vue 文件对应的 loader。

module: {
    rules: [
        {
            test: /\.vue$/,
            use: ["vue-loader"]
        }
    ]
}

Webpack2 必须在 module.rules 下配置 loader。'-loader'不能省略,必须将 loader 名写全。可使用 Rule.use 或 Rule.loader 来配置 loader(Rule.loader 是 Rule.use: [ { loader } ] 的简写),建议用 use。

上面完成了新增页面及访问该页面所需的配置,下面来测试下是否能正常访问/index。执行npm run dev,浏览器显示如图界面。

支持CSS

安装 css-loader 后便可在 vue 文件中使用

npm i css-loader -D

想要支持import / require引入CSS文件,则须要配置对应的 Rule。

{
    test: /\.css$/,
    use: ["vue-style-loader", "css-loader"]
}

 

<script>
import "../style/style.css"
</script>

支持CSS预处理语言

以 stylus 为例,安装 stylus 及 stylus-loader。

npm install stylus stylus-loader -D

增长 .styl 文件对应的 loader 配置。

{
    test: /\.styl$/,
    use: ["vue-style-loader", "css-loader", "stylus-loader"]
}

  使用示例:

<style lang="stylus">
    .stylus
        .red
            color red
</style>
<script>
    import "../css/stylus-example.styl"
</script>

  

node-sass 安装慢的解决办法

使用淘宝镜像:npm set disturl https://npm.taobao.org/dist

也能够单独设置node-sass镜像:npm set sass_binary_site http://cdn.npm.taobao.org/dist/node-sass

支持图片及图标字体

安装图片及图标字体依赖的loader。

npm install url-loader file-loader -D

  增长图片及图标字体的loader配置。

{
    test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
    use: [{
        loader: "url-loader",
        options: {
            limit: 10000,
            name: 'images/[name].[hash:7].[ext]'    // 将图片都放入images文件夹下,[hash:7]防缓存
        }
    }]
},
{
    test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
    use: [{
        loader: "url-loader",
        options: {
            limit: 10000,
            name: 'fonts/[name].[hash:7].[ext]'    // 将字体放入fonts文件夹下
        }
    }]
}

  

构建

添加打包命令以下:

"build":"webpack --progress --colors"

  执行npm run build开始构建,完成后,能够看到工程目录下多了dist目录以及 dist/build.js。

 

使用 Webpack 插件

压缩JS

在以前的文章提到过,打开未压缩版的build.js,你会发现ES6的语法没有被转化为ES5,所以须要安装babel 套件来完成语法的转化,不然压缩的时候就会报错。以前普遍使用的转码规则为 babel-preset-es2015,但 Babel 的官网上在9月宣布 ES2015 / ES2016/ ES2017 等等 ES20xx 时代的 presets 统统被废弃(deprecated),取而代之的是 babel-preset-env,而且承诺它将成为“将来不会过期的(future-proof)”解决方案。

npm i babel-loader babel-core babel-preset-env -D

  增长babel的配置文件.babelrc

{
    "presets": [
        ["env", { "modules": false }]
    ],
    "comments": false
}

  

将 modules 设置为 false,即交由 Webpack 来处理模块化,经过其 TreeShaking 特性将有效减小打包出来的 JS 文件大小,能够自行对比下先后打包出来的文件的大小,效果仍是不错的。

comments 便是否保留注释。

接着配置 JS 文件的 loader。

{
    test: /\.js$/,
    use: "babel-loader",
    include: [path.resolve(__dirname, 'src')]
}

  

注意:Webpack2建议尽可能避免exclude,更倾向于使用include。

压缩 JS 采用webpack.optimize.UglifyJsPlugin,配置以下:

new webpack.optimize.UglifyJsPlugin()

  官网称warnings默认为false,你可能会遇到即便没有配置warnings: true,控制台仍显示警告,看下面这段源码就知道了。查看源码

只有当options.compress !== false时 warnings 才会被设置默认值 false,因此一旦配置了 compress 其它选项,那就需同时配置warnings: false

warnings做用是当插件在压缩过程当中移除的无效代码或定义是显示警告信息(display warnings when dropping unreachable code or unused declarations etc.)。

提取CSS

使用extract-text-webpack-plugin插件提取CSS。更改 css 及 less 的 loader 配置以下。

// 安装插件
npm i extract-text-webpack-plugin -D

  

// var ExtractTextPlugin = require("extract-text-webpack-plugin")
{
    test: /\.css$/,
    use: ExtractTextPlugin.extract({
        use: "css-loader"
    })
},
{
    test: /\.styl$/,
    use: ExtractTextPlugin.extract({
        use: ["css-loader", "stylus-loader"]
    })
}

  上述配置并不能提取 vue 文件中的 style,须要设置 vue-loader 参数才能够。

{
    test: /\.vue$/,
    use: {
        loader: "vue-loader",
        options: {
            loaders: {
                css: ExtractTextPlugin.extract({
                    use: 'css-loader'
                }),
                stylus: ExtractTextPlugin.extract({
                    use: ["css-loader", "stylus-loader"]
                })
            }
        }
    }
}

  初始化插件,filename 能够指定 CSS 文件的目录。

new ExtractTextPlugin({
    filename: "css/style.css"
})

  

PostCSS

安装 postcss-loader 及 postcss 插件。

npm i postcss-loader cssnano -D

  配置 loader 以下:

// css-loader配置改成
use: ['css-loader', "postcss-loader"]
// stylus-loader配置改成
use: ["css-loader", "postcss-loader", "stylus-loader"]

  

postcss-loader 要放在 css-loader 和 style-loader 以后,CSS 预处理语言 loader 以前(stylus-loader)。

新增 postcss.config.js 来配置postcss插件,这样就不用给每一个 postcss-loader 去配置。更多 postcss-loader 的配置方式请参考 postcss-load-config

module.exports = {
    plugins: [
        require('cssnano')
    ]
}

  cssnano 使用了一系列 postcss 插件,包含了经常使用的 autoprefixer 等,如何传入 autoprefixer 的配置?

require('cssnano')({
    autoprefixer: {
        add: true,
        browsers: ['> 5%']
    }
})

  其中有一个插件 postcss-zindex 使用中发现有些问题。若是想禁用这个插件的话,配置以下:

require('cssnano')({
    zindex: {
        disable:true
    }
})

  附:postcss插件分类搜索网站:http://postcss.parts/

生成首页

安装 html-webpack-plugin 插件。

npm i html-webpack-plugin -D

  初始化插件。

// var HtmlWebpackPlugin = require('html-webpack-plugin');
new HtmlWebpackPlugin({
    filename: 'index.html',
    template: 'index.tpl.html'
})

  

其它插件

Webpack3 新增的做用域提高。

new webpack.optimize.ModuleConcatenationPlugin()

  指定生产环境,以便在压缩时可让 UglifyJS 自动删除代码块内的警告语句。

new webpack.DefinePlugin({
    'process.env.NODE_ENV': JSON.stringify('production')
})

  

由于这个插件直接作的文本替换,给定的值必须包含字符串自己内的实际引号。一般,有两种方式来达到这个效果,使用 '"production"', 或者使用 JSON.stringify('production')。

你彻底能够在本身的代码中使用process.env.NODE_ENV来区分开发和生产,从而针对不一样的环境作一些事情。不用担忧这部分代码会被保留,最终会被 UglifyJS 删除。例如:

if (process.env.NODE_ENV != "production") {
    // 开发环境
}
// webpack.DefinePlugin插件替换后,上述代码会变成
if ("production" != "production") {
    // 开发环境
}
// 输出
if (false) {
    // 开发环境
}
// UglifyJS 会删除这段无效代码

  

使用上述插件后再次构建,会发现生成的JS相比原来的体积小了很多。

friendly-errors-webpack-plugin 是一个更友好显示 webpack 错误信息的插件。插件 github 地址:https://github.com/geowarin/friendly-errors-webpack-plugin

通常在开发环境下使用。

var FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin');

var webpackConfig = {
  // ...
  plugins: [
    new FriendlyErrorsWebpackPlugin(),
  ],
  // ...
}

  效果以下图:

显示构建进度插件:webpack.ProgressPlugin

{
  // ...
  plugins: [
    new webpack.ProgressPlugin(),
  ],
  // ...
}

  效果以下图:

美化 webpack 编译控制台打印的信息的插件webpack-dashboard

 

分离Webpack配置

将开发和生产配置文件分离,方便增长各个环境下的个性配置。Webpack2文档中也详细阐述了如何为多环境配置webpack。基本思路以下:

  • 编写一个基本配置文件(webpack.base.config.js)
  • 使用webpack-merge合并这个基础配置和针对环境的特定的配置(webpack.dev.config.js,webpack.prod.config.js)

webpack.base.config.js 内容以下:

var webpack = require('webpack');
var path = require('path');
var utils = require('./utils');
function resolve(relPath) {
    return path.resolve(__dirname, relPath);
}
module.exports = {
    entry: { app: resolve('../src/main.js') },
    output: {
        filename: 'js/[name].js'
    },
    module: {
        rules: [{
                test: /\.js$/,
                use: "babel-loader",
                include: [resolve('../src')]
            },
            {
                test: /\.vue$/,
                use: {
                    loader: "vue-loader",
                    options: utils.vueLoaderOptions()
                }
            },
            {
                test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
                use: {
                    loader: "url-loader",
                    options: {
                        limit: 10000,
                        name: 'images/[name].[hash:7].[ext]'
                    }
                }
            },
            {
                test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
                use: [{
                    loader: "url-loader",
                    options: {
                        limit: 10000,
                        name: 'fonts/[name].[hash:7].[ext]'
                    }
                }]
            }
        ]
    }
}

  

为何要将vue-loader的options提取出来?其主要是用来配置CSS及CSS预处理语言的loader,开发环境能够不用配置,可是生产环境须要提取CSS、增长postcss-loader等,所以须要提取出来针对不一样环境返回相应的options。后面会列出utils.vueLoaderOptions的内容。

为何没有配置CSS的loader?理由和上面的vue-loader同样。

为何没有配置path和publicPath?一方面是个性化参数,另外开发和生产可能不相同,所以在webpack.dev.config和webpack.prod.config中分别配置更为合适。

webpack.dev.config.js 内容以下:

var webpack = require('webpack');
var merge = require('webpack-merge');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var baseWebpackConfig = require('./webpack.base.config');
var utils = require('./utils');
var config = require('./config');
Object.keys(baseWebpackConfig.entry).forEach(function(name) {
    baseWebpackConfig.entry[name] = [
        `webpack-dev-server/client?http://localhost:${config.dev.port}/`,
        "webpack/hot/dev-server"
    ].concat(baseWebpackConfig.entry[name])
});
module.exports = merge(baseWebpackConfig, {
    output: {
        path: config.dev.outputPath,
        publicPath: config.dev.outputPublicPath
    },
    module: {
        rules: utils.styleLoaders()
    },
    plugins: [
        new webpack.HotModuleReplacementPlugin(),
        new HtmlWebpackPlugin({
            filename: 'index.html',
            template: 'index.html',
            inject: true
        })
    ]
})

  

添加了HtmlWebpackPlugin后,index.html中就不须要在本身去引用打包的JS了,会自动根据打包的JS添加引用,这样更加方便,关于HtmlWebpackPlugin的配置,须要说明两点:

  1. template的路径是相对于webpack编译时的上下文目录,说白了就是项目根目录,所以上面能够直接配置index.html,其指向的就是根目录下的index.html;
  2. filename则是相对于webpack配置项output.path(打包资源存储路径)。

html-webpack-plugin关于template和filename路径源码以下:

// https://github.com/jantimon/html-webpack-plugin/blob/master/index.js
// template
this.options.template = this.getFullTemplatePath(this.options.template, compiler.context);
// filename
this.options.filename = path.relative(compiler.options.output.path, filename);

  config.js内容以下:

module.exports = {
    dev: {
        outputPath: path.resolve(__dirname, '../static'),
        outputPublicPath: '/',
        port: 8000
    },
    prod: {
        outputPath: path.resolve(__dirname, '../static'),
        outputPublicPath: '/static/'
    }
}

  utils.js内容以下:

var ExtractTextPlugin = require('extract-text-webpack-plugin');
var isProd = process.env.NODE_ENV === "production";
// 根据项目需求添加CSS预处理语言并安装相应的loader,以stylus-loader为例
var cssLang = [{
    name: 'css',
    reg: /\.css$/,
    loader: 'css-loader'
}, {
    name: 'stylus',
    reg: /\.styl$/,
    loader: "stylus-loader"
}];
function genLoaders(lang) {
    var loaders = ['css-loader', 'postcss-loader'];
    if (lang.name !== 'css') {
        loaders.push(lang.loader);
    }
    if (isProd) {
        // 生产环境须要提取CSS
        loaders = ExtractTextPlugin.extract({
            use: loaders
        });
    } else {
        // 开发环境须要vue-style-loader将CSS提取到页面头部
        loaders.unshift('vue-style-loader');
    }
    return loaders;
}
// 各类CSS的loader
exports.styleLoaders = function() {
    var output = [];
    cssLang.forEach(lang => {
        output.push({
            test: lang.reg,
            use: genLoaders(lang)
        })
    })
    return output;
};
// vue-loader的options
exports.vueLoaderOptions = function() {
    var options = {
        loaders: {}
    };
    cssLang.forEach(lang => {
        options.loaders[lang.name] = genLoaders(lang);
    });
    return options;
}

  接下来就是如何启动webpack-dev-server,vue-cli的webpack模板工程用的express及webpack中间件作开发服务器,其实用webpack-dev-server就能知足需求,固然用express可以作更多的事情,毕竟webpack-dev-server是一个轻量级的express。dev.js内容以下:

var webpack = require('webpack');
var webpackDevServer = require('webpack-dev-server');
var devConfig = require("./webpack.dev.config");
var config = require("./config");
var compiler = webpack(devConfig);
var server = new webpackDevServer(compiler, {
    hot: true,
    noInfo: true,
    publicPath: config.dev.outputPublicPath,
    stats: { colors: true }
});
server.listen(config.dev.port, "0.0.0.0");
var url = `http://localhost:${config.dev.port}/`;
// 需先安装 opn 模块 npm i opn -D
var opn = require('opn');
// 打包完毕后启动浏览器
server.middleware.waitUntilValid(function() {
    console.log(`> Listening at ${url}`);
    opn(`${url}`);
})

  生产配置文件(webpack.prod.config.js)内容以下:

// 设定为生产环境
process.env.NODE_ENV = 'production';
var webpack = require('webpack');
var merge = require('webpack-merge');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var baseWebpackConfig = require('./webpack.base.config');
var utils = require('./utils');
var config = require('./config');

module.exports = merge(baseWebpackConfig, {
    output: {
        path: config.prod.outputPath,
        publicPath: config.prod.outputPublicPath
    },
    module: {
        rules: utils.styleLoaders()
    },
    plugins: [
        new webpack.DefinePlugin({
            'process.env.NODE_ENV': '"production"'
        }),
        new webpack.optimize.UglifyJsPlugin(),
        new ExtractTextPlugin({
            filename: "css/style.css?[contenthash:8]"
        }),
        new HtmlWebpackPlugin({
            filename: 'index.html',
            template: 'index.html',
            inject: true
        })
    ]
})

  生产构建(prod.js)内容以下:

var webpack = require("webpack");
var webpackProdConfig = require('./webpack.prod.config');
webpack(webpackProdConfig, function(err, stats) {
    process.stdout.write(stats.toString());
});

  对应在package.json中添加开发和生产构建的命令以下:

"scripts": {
    "dev": "node build/dev.js",
    "build": "node build/prod.js"
}

  

代码分割

参照vue-cli及webpack文档的提取公共代码的方式,利用插件webpack.optimize.CommonsChunkPlugin未来自node_modules下的模块提取到vendor.js(通常来说都是外部库,短期不会发生变化)。因而有以下代码:

new webpack.optimize.CommonsChunkPlugin({
    name: 'vendor',
    minChunks: function(module, count) {
        return module.resource && /\.js$/.test(module.resource) && module.resource.indexOf(path.join(__dirname, '../node_modules')) === 0
    }
})

  若是你查看未压缩版的vendor.js,会发现不只包含vue的代码,还包含了webpackBootstrap(webpack模块加载器)的代码,按理说放在vendor里面也没啥问题,毕竟后面的模块都须要依赖于此,可是若是你的chunk使用了hash,一旦app代码发生了改变,相应的hash值会发生变化,webpackBootstrap的代码也会发生变化(如图),而咱们提取vendor的初心就是这部分代码改变频率不大,显然这不符合咱们的目标,那么应当将webpackBootstrap再提取到一个文件中,代码以下:

new webpack.optimize.CommonsChunkPlugin({
    name: 'manifest',
    chunks: ['vendor']
})

  

用 import 仍是 require

vue 2.2.0 后不能直接使用 require 来加载 vue,那么到底改是该使用 ES6 Module 仍是 CommonJS 呢?,先看一个对比图:

所有使用 ES6 Module,即import、export default,最终打包的app.js为1.20KB。

所有使用 CommonJS(除了vue),即require、module.exports,最终打包的app.js为1.12KB。

虽然二者大小相差不大,可是这不由让人以为用 CommonJS 彷佛是一条优化措施,那么代码层面究竟是怎么回事呢?

为了一探究竟,注释了压缩插件,以about.vue最终构建的代码来看,使用 CommonJS 的代码以下:

(function (module, exports, __webpack_require__) {
  "use strict";
  module.exports = {
    data: function data() {
      return {
        page: 'about'
      };
    }
  };
}),

  这基本上和 about.vue 中的代码相差无几,而使用 ES6 Module 构建的代码以下:

(function (module, exports, __webpack_require__) {
  "use strict";
  Object.defineProperty(exports, "__esModule", {
    value: true
  });
  exports.default = {
    data: function data() {
      return {
        page: 'about'
      };
    }
  };
}),

  

对比两种规范下构建后的代码,使用 ES6 Module 主要是多了 Object.defineProperty 那一部分,从而致使了最终打包的文件大一点儿。那是否是为了最终文件体积能小些就所有使用 CommonJS 呢?这个须要你充分理解 CommonJS 和 ES6 Module 的区别,引用《ECMAScript 6入门》这本书的解释以下:

ES6模块加载的机制,与CommonJS模块彻底不一样。CommonJS模块输出的是一个值的拷贝,而ES6模块输出的是值的引用。

为了更清楚的理解这段话,咱们稍微作一下测试,在 src 下增长一个test.js,内容以下(分别将两种写法列出):

// CommonJS
var counter = 1;
function incCounter() {
    counter++;
}
module.exports = {
    counter: counter,
    incCounter: incCounter,
};
// ES6 Module
export let counter = 1;
export function incCounter() {
    counter++;
}

  在about.vue中测试以下:

// CommonJS
var test = require('../test');
export default {
    created() {
        console.log(test.counter); // 1
        test.incCounter();
        console.log(test.counter); // 1
    }
}
// ES6 Module
import { counter, incCounter } from '../test';
export default {
    created() {
        console.log(counter); // 1
        incCounter();
        console.log(counter); // 2
    }
}

  最终的输出值也印证了前面的解释,更详细的解读请查阅相关资料或书籍。只有深刻理解了二者的区别,天然就能明白什么时候该使用何种规范。固然 ES6 做为将来的趋势,咱们应该去作更多的尝试。

异步组件(懒加载)

以前用懒加载的方式是:require.ensure,在 webpack2 中引入了 Code Splitting-Async 的新方法 import(),用于动态引入 ES Module。require.ensure 能够指定 chunkFilename,可是 import 没有很好的途径去指定,webpack3 为了解决这个问题,提出了用魔法注释的方式来指定模块名。

结合 vue-router 能够轻松实现懒加载,配置路由指向异步组件便可实现懒加载,好比:

{
        path: '/async',
        component: () => import(/* webpackChunkName: "async" */'./views/async.vue')
}

  

若是你发现使用 import() 运行时报错,有几种状况:

  1. babel presets 配置为 es2015,则不支持动态导入功能,所以须要安装支持动态导入的 presets(npm i babel-preset-stage-2 -D),或者 babel 插件(npm i babel-plugin-syntax-dynamic-import -D);
  2. babel presets 配置为 env,可是 modules 配置为 false,babel 则不会解析 import,能够安装插件 (npm i babel-plugin-syntax-dynamic-import -D)解决。

 

魔法注释虽然指定了块名,可是 webpack 默认的块名配置为 [id].js,即用的模块的 id 做为块名,所以须要咱们手动改下配置。

修改 webpack.base.config.js 的 output:

output: {
    filename: 'js/[name].js',
    chunkFilename: "js/[name].[chunkhash].js"
}

  效果以下图:

若是发现魔法注释没有生效,要检查下 .babelrc 的配置项 comments 是否设为 true。或者不配置 comments(默认为true)

extract-text-webpack-plugin 默认不会提取异步模块中的 CSS,须要加上配置:

new ExtractTextPlugin({
    allChunks:true,
    filename: "css/style.css?[contenthash:8]"
})

  

 http://www.qinshenxue.com/article/20161118151423.html

相关文章
相关标签/搜索