从搭建vue-脚手架到掌握webpack配置(三.多页面构建)

前言

上一期中咱们经过引入了插件实现了很多功能——样式抽离、公共模块提取、代码压缩等等;本期开始讲讲可能会用到的第三方编译器的配置和多入口的优化。css

本期重点:postcss和.babelrc配置多入口多页面代码提取优化html

往期连接:
从搭建vue-脚手架到掌握webpack配置(一.基础配置)
从搭建vue-脚手架到掌握webpack配置(二.插件与提取)vue

小小题外话

本系列文章写到了第三篇,Jason我发现了这系列的文章有一个很大的缺陷,文章和前面的文章存在着耦合,可能会致使知识点存在线性的关联,对于有基础的朋友来讲,又要从第一期开始阅读的话,比较不友好。
因此后面的文章开始Jason会尝试独立知识点,尽可能回归知识点的运用上;在文章的开头也会说明本期的主要内容;固然一些插件和loader的进阶使用仍是要有基础的,初学者仍是建议从头过一遍。node

使用postcss

postcss介绍

postcss官方的GitHub上还有中文的介绍。react

PostCSS 是一个容许使用 JS 插件转换样式的工具。 这些插件能够检查(lint)你的 CSS,支持 CSS Variables 和 Mixins, 编译还没有被浏览器普遍支持的先进的 CSS 语法,内联图片,以及其它不少优秀的功能。webpack

简单来讲postcss就是一个css的转换器,有了postcss或许你就不用再用less和sass了,经过在postcss上添加插件能够组装出你须要的语法需求和功能(属性变量,父子嵌套,版本兼容等),在postcss上一般会用的插件有cssnext、Autoprefixer、postcss-import。甚至能够在postcss上用less或sass编译器git

用法

这里就用添加Autoprefixer(自动兼容浏览器)为例简单讲讲postcss的配置方法,有几种配置方法es6

  • 使用配置文件配置 postcss.config.js或.postcssrc.js
  • 使用post-loader的时候经过options配置项配置

建议是使用配置文件进行配置,这样能够在全部调用到postcss-loader的地方使用一样的配置github

!值得一提的是其实vue-loader是默认启用了postcss-loader的,因此vue-loader的官方文档里面有直接设置postcss配置项的选项。web

因此在有vue-loader的项目里面实际上是不用手动安装npm install -save-dev postcss-loader的,不是vue项目就装吧。

只要在css的loaders里面添加vue-style-loader,就会自动启用postcss了,以下

{
    test:/\.less$/,
    use:[
        'vue-style-loader''css-loader',
        'less-loader'
    ]
    })
},
{
    test:/\.vue$/,
    loader:'vue-loader',
    options:{
        loaders:{
            'css': [
                'vue-style-loader''css-loader',
            ],
            'less': [
                'vue-style-loader''css-loader',
                'less-loader'
            ]
        },
        //postcss:{}//vue-loader还自带了这一选项,可是建议用配置文件进行配置
    }
}
复制代码

引入 Autoprefixer :

  • 须要安装 autoprefixer插件哦:npm install --save-dev autoprefixer
  • 新建.postcssrc.js文件在跟目录下,内容以下
module.exports = {
  "plugins": {
    //"postcss-import": {},
    // to edit target browsers: use "browserslist" field in package.json
    "autoprefixer": {}
  }
}
复制代码

autoprefixer对应的对象{},是该插件的配置项,有须要能够查阅文档进行配置;postcss-import插件是@import是能够引本地的文件,如node_modules内的文件,英文好的去看看该插件介绍。既然vue-cli用了postcss-import咱们也这样用吧,记得安装npm install --save-dev postcss-import

autoprefixer会去检查package.json里的browserslist 配置项做为兼容浏览器版本的范围

//在package.json上添加这一项
"browserslist": [
    "> 5%",
    "last 2 versions",
    "not ie <= 8"
  ]
复制代码

咱们这里就简单的引入了autoprefixer,没有作过多的配置,若是对postcss感兴趣的话能够去看看下面文档

.babelrc配置

在使用es6语法编写js的时候,咱们都会在webpack上用babel-loader转换js文件,而.bablrc就是babel编译js时用的的规则和插件的配置规范

像postcss同样在根目录下建立名为.babelrc的文件(没有.js后缀)

{
  "presets": [
    ["env", {
       "modules": false ,
       "targets": {
        "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
      }
    }],
    "stage-3"
  ]
}
复制代码

安装依赖:

npm install --save-dev babel-preset-env babel-preset-stage-0 
复制代码

babel-preset-env,本依赖插件是设置编译环境的插件,若是直接引入不设置规则那实际它和引入babel-preset-latest同样,会囊括es201五、es201六、es2017。

'modules':false:设置模块引用规则,能够设置成"amd" | "umd" | "systemjs" | "commonjs" | false, defaults to "commonjs",设置了false,就是用es6以上默认的规则。

targets.browsers:设置兼容的浏览器范围

tage-3依赖,ES7不一样阶段语法提案的转码规则(共有4个阶段),选装一个

babel-preset-stage-0  babel-preset-stage-1
babel-preset-stage-2  babel-preset-stage-3
复制代码
其余

还能够在里面添加插件实现更多的功能,如添加react的jsx编译。像官方给出的示例代码同样

{
  "presets":["env"]
  "plugins": ["transform-react-jsx"],
  "ignore": [
    "foo.js",
    "bar/**/*.js"
  ]
}
复制代码

毕竟这不是babelrc的教程,更高级的使用能够去如下的连接去深刻学习。

连接

好了,整个个系列到目前为止经常会用到的webpack配置、loader、插件、编译器都使用过了。
后面开始就要按照需求,改进咱们的自动化构建配置了。

多页面、多入口

不少用vue或者react工程环境都会默认是spa(单页应用),可是业界上也有不少项目倾向于使用vue等框架的组件化功能,可是并不引入router模块,而是使用传统的路由连接或者二者混合;尤为是视频网站、购物网站等pc端的网页是不会作成spa的。因此在一个工程中编辑和生成多个页面是必须的状况。

在src目录下新建home.js文件(内容和main.js同样就行),在webpack的entry项设置多个入口是最方便快捷的方法。

entry:{
    app:'./src/main.js',
    home:'./src/home.js'
},
output:{
    path:path.resolve(__dirname,'./dist'),
    filename:"js/[name].js",
},
复制代码

entry对应不一样的页面设置不一样的入口文件,output.filename要写成[name]以chunk名命名的形式

看似简单,可是咱们以前用到公共代码提取(CommonsChunkPlugin)和css抽离(ExtractTextPlugin)会把整个项目中的代码打包到一块儿会影响页面的加载。因此咱们要改进他的逻辑

css分页面拆分

这个很简单,只要在实例化ExtractTextPlugin插件的同时添加一个allchunks:true参数就能够了,还有就是把输出都指向一个css文件,用[name]区分chunk名

const ExtractVueCss = new ExtractTextPlugin({filename:'styles/[name]-style.css',allChunks:true});
复制代码

按照个人习惯,先分出一份公共样式(root.css),其余的按chunk的数量进行分割,因此root.css的allchunks:false,其余的true

const path = require('path')
const webpack = require('webpack')
const ExtractTextPlugin = require("extract-text-webpack-plugin")
const ExtractRootCss = new ExtractTextPlugin({filename:'styles/[name]-root.css',allChunks:false});
const ExtractVueCss = new ExtractTextPlugin({filename:'styles/[name]-style.css',allChunks:true});

module.exports = {
    //other options...
    module:{
        rules:[
        //...
            {
                test:/\.css$/,
                //这里用的ExtractRootCss,输出到root.css
                use:ExtractRootCss.extract({
                    fallback:'style-loader',
                    use:['css-loader']
                })
            },
            {
                test:/\.less$/,
                //这里用的ExtractRootCss,输出到root.css
                use:ExtractRootCss.extract({
                    fallback:'style-loader',
                    use:[
                        'css-loader',
                        'less-loader'
                    ]
                })
            },
            {
                test:/\.vue$/,
                loader:'vue-loader',
                options:{
                    loaders:{
                        //这里用的ExtractVueCss
                        'css': ExtractVueCss.extract({
                            use: 'css-loader',
                            fallback: 'vue-style-loader' 
                          }),
                        //这里用的ExtractVueCss
                        'less':
                        ExtractVueCss.extract({
                            use:[
                                'css-loader',
                                'less-loader'
                            ],
                            fallback:'vue-style-loader'
                        })
                    },
                }
            },
        ]
    },
    plugins:[
        //填入插件实例,记得按顺序填入
        ExtractRootCss,//root.css
        ExtractVueCss,//vue内的css
        new webpack.HotModuleReplacementPlugin(),
    ]
}
复制代码

多页面公共提取

不懂commons-chunk-plugin,又不想看之前的文章的话看左边连接

以前咱们把node_modules内的模块抽取到了vender.js里面。

//抽取从node_modules引入的模块,如vue,vue-router
new webpack.optimize.CommonsChunkPlugin({
    name: 'vender',
    minChunks:function(module,count){
        var sPath = module.resource;
        // console.log(sPath,count);
        //匹配 node_modules文件目录
        return sPath &&
            /\.js$/.test(sPath) &&
            sPath.indexOf(
                path.join(__dirname, 'node_modules')
            ) === 0
    }
}),
复制代码

上上面设置了多个入口chunk,并且output的时候也不是设置成惟一的文件名,这样webpack打包的时候会自动按照入口chunk的数量生成相应数量的代码包,按目前状况会有app.js和home.js。

可是问题在于每一个chunk都会把全部他们用到的模块单独打包起来。好比app和home用到同样的header.vue模块,webpack都会分别打包到app.js和home.js里面。屡次复用的模块最好是能够抽取出来,在首页加载过js文件以后获得了缓存,在详情页能立刻获得提高页面加载速度。

为了解决以上问题,咱们再加一个公共代码提取的实例

new webpack.optimize.CommonsChunkPlugin({
    name:'common'
    minChunks:2
}),
复制代码

这样每一个页面(入口chunk)中引入超过两次的模块就会打包到common.js文件下面。

minChunks2表明全部chunk中复用超过2以上的模块会被提取。写成2粒度最小,你能够按本身需求修改。

(5.20更新) 实际项目上发现,单一入口的时候,vendor.js并无被抽离,缘由多是单一入口时common没被提早致使的(具体缘由请大神指教),因此咱们要再作一步入口数量判断

Object.keys(config.page).length >= 2 
    ? new webpack.optimize.CommonsChunkPlugin({
            name: 'common',
            minChunks:2
        }):()=>{},
复制代码
抽取webpack的运行时逻辑

参考vue-cli,咱们会认为webpack的加载调度等运行时(runtime)逻辑是不会频繁修改的,因此咱们把这部分抽离出来方面之后的页面都调用它。这样的话咱们在加一个CommonsChunkPlugin实例,用于抽取这些逻辑。

Object.keys(config.pages).length >= 2 ? 
    new webpack.optimize.CommonsChunkPlugin({
        name: 'common',
        minChunks:2
    }):()=>{},
new webpack.optimize.CommonsChunkPlugin({
    name: 'vender',
    minChunks:function(module,count){
        var sPath = module.resource;
        return sPath &&
            /\.js$/.test(sPath) &&
            sPath.indexOf(
                path.join(__dirname, '../node_modules')
            ) === 0
    }
}),
//将webpack runtime 和一些复用部分抽取出来
new webpack.optimize.CommonsChunkPlugin({
    name: 'manifest',
    minChunks:Infinity
}),
复制代码

!注意!这里插件是有引入顺序的,顺序不对可能会致使操做被覆盖。

minChunksInfinity什么chunk都不抽取出来,只抽取webpack的runtime等逻辑。

抽取异步公共模块

在开发vue或者react的时候可能会用到异步加载模块的能力(又叫懒加载),好比import()webpack require.ensure功能异步加载的模块。vue项目一般是vue-router懒加载组件的时候用到。

虽然作多页开发的时候不必定会用到vue-router,可是咱们让构建配置更健壮那就适配到spa的状况,把异步公共模块的提取也加进去

// 放到上面三个CommonsChunkPlugin的最后

new webpack.optimize.CommonsChunkPlugin({
    // names: ["app", "subPageA"]
    // (选择 chunks,或者忽略该项设置以选择所有 chunks)
    async: 'vendor-async',
    children: true,
    minChunks:2
}),
复制代码

没有给name的话就会默认选择全部入口chunk。

async:可使true或者字符串,字符串的话就是生成的公共chunk的名字。(这个选项一直没搞懂,直到看到了这文章Webpack 大法之 Code Splitting

children:选择全部被选 chunks 的子 chunks

minChunks:大于等于两个chunk复用的子模块会提取到该公共chunk

中文文档下面的示例有介绍 link

改写生成的html模板

一样,懒得看上期文章,又不懂HtmlWebpackPlugin的同窗点这里

HtmlWebpackPlugin插件在上一期中用到了,可是咱们只是简单的设置了模板文件和出口文件。插件会默认把全部输出的chunk包和css文件都引入到生成的模板中。咱们以下修改一下插件的配置

plugins:[
        new HtmlWebpackPlugin({
            filename:'index.html',
            title:'vue demo',
            // favicon:'./src/images/logo.png',
            template:'./index.html',
            chunks:['app','vender','manifest','common'],
            chunksSortMode: 'dependency'
        }),
        new HtmlWebpackPlugin({
            filename:'home.html',
            title:'vue home',
            template:'./index.html',
            chunks:['home','vender','manifest','common'],
            chunksSortMode: 'dependency'
        }),
    ]
复制代码

filename:生成的文件名,区分页面起对应的名字

template:html模板来源,应为是vue 项目因此用一样的模板,你能够按本身的须要来设置

chunks:关键的来了,这个就是把本模板关联的chunks列举出来的参数,我把各类的入口chunk和提取出来的公共chunk填入了。

chunksSortMode:chunk的引入顺序,'dependency'按依赖关系引入

build一下

运行npm run build,生成的dist文件目录结构以下

dist目录

并无成产vendor-async.js异步公共模块是由于项目中尚未用到异步加载的部分

到目前为止完整的webpack.config.js文件能够到这 下载

ps

Jason建议看别人的webpack配置时遇到不懂的插件多多查 npmjs 或者社区论坛,有时间去看看webpack官方介绍的插件或许里面有有你须要的
Jason水平有限,若是有什么地方的知识点有错误请你们多多提点,在评论中告诉我。

下期预告

上一期的webpack配置完后彻底能够应付单页应用的构建,而这一期还适应了多页面的构建需求,并且还学会了postcss和babelrc的基础配置方法。是否是感受愈来愈接近vue-cli建立的项目了呢?

引入了多页面的构建思想后,咱们发现若是咱们的页面不断的增长就要不断的给webpack.config.js添加插件和逻辑。因此为了方便和易用性,下一期咱们来尝试写一些封装逻辑把构建配置封装起来,用一个文件整合经常使用的配置项统一对工程进行配置。

下一期可能不会很快能更新,一方面由于手上有事情要忙,另外一方面整项目我尚未封装测试好(这也正是为何一直没有给出github的缘由),因此请有关注本系列的同窗可能要等等了,有须要的同窗也能够关注个人帐号留意更新。

下一期已更新:从搭建vue-脚手架到掌握webpack配置(四.自动化封装)

参考

github.com/postcss/pos…

PostCSS配置指北.md

PostCSS 是个什么鬼东西?

html-webpack-plugin用法全解

相关文章
相关标签/搜索