從搭建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用法全解

相關文章
相關標籤/搜索