前言
上一期中我們通過引入了外掛實現了不少功能——樣式抽離、公共模組提取、程式碼壓縮等等;本期開始講講可能會用到的第三方編譯器的配置和多入口的優化。
本期重點:postcss和.babelrc配置、多入口多頁面程式碼提取優化
往期連結:
從搭建vue-腳手架到掌握webpack配置(一.基礎配置)
從搭建vue-腳手架到掌握webpack配置(二.外掛與提取)
小小題外話
本系列文章寫到了第三篇,Jason我發現了這系列的文章有一個很大的缺陷,文章和前面的文章存在著耦合,可能會導致知識點存線上性的關聯,對於有基礎的朋友來說,又要從第一期開始閱讀的話,比較不友好。
所以後面的文章開始Jason會嘗試獨立知識點,儘量迴歸知識點的運用上;在文章的開頭也會說明本期的主要內容;當然一些外掛和loader的進階使用還是要有基礎的,初學者還是建議從頭過一遍。
使用postcss
postcss介紹
postcss官方的GitHub上還有中文的介紹。
PostCSS 是一個允許使用 JS 外掛轉換樣式的工具。 這些外掛可以檢查(lint)你的 CSS,支援 CSS Variables 和 Mixins, 編譯尚未被瀏覽器廣泛支援的先進的 CSS 語法,內聯圖片,以及其它很多優秀的功能。
簡單來說postcss就是一個css的轉換器,有了postcss或許你就不用再用less和sass了,通過在postcss上新增外掛可以組裝出你需要的語法需求和功能(屬性變數,父子巢狀,版本相容等),在postcss上通常會用的外掛有cssnext、Autoprefixer、postcss-import。甚至可以在postcss上用less或sass編譯器
用法
這裡就用新增Autoprefixer(自動相容瀏覽器)為例簡單講講postcss的配置方法,有幾種配置方法
- 使用配置檔案配置 postcss.config.js或.postcssrc.js
- 使用post-loader的時候通過options配置項配置
建議是使用配置檔案進行配置,這樣可以在所有呼叫到postcss-loader的地方使用同樣的配置
!值得一提的是其實vue-loader是預設啟用了postcss-loader的,所以vue-loader的官方文件裡面有直接設定postcss配置項的選項。
所以在有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感興趣的話可以去看看下面文件
- postcss的github(有常用外掛的連結和介紹)
- postcss-loader配置說明(無論是用檔案配置還是webpack內配置都用一樣的配置規則)
- autoprefixer配置項介紹
- browserslist的介紹
.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
一樣,會囊括es2015、es2016、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檔案下面。
minChunks
:2
代表所有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
}),
複製程式碼
!注意!這裡外掛是有引入順序的,順序不對可能會導致操作被覆蓋。
minChunks
:Infinity
什麼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檔案目錄結構如下
並沒有成產vendor-async.js非同步公共模組是因為專案中還沒有用到非同步載入的部分
到目前為止完整的webpack.config.js檔案可以到這 下載
ps
Jason建議看別人的webpack配置時遇到不懂的外掛多多查 npmjs 或者社群論壇,有時間去看看webpack官方介紹的外掛或許裡面有有你需要的
Jason水平有限,如果有什麼地方的知識點有錯誤請大家多多提點,在評論中告訴我。
下期預告
上一期的webpack配置完後完全可以應付單頁應用的構建,而這一期還適應了多頁面的構建需求,而且還學會了postcss和babelrc的基礎配置方法。是不是感覺越來越接近vue-cli建立的專案了呢?
引入了多頁面的構建思想後,我們發現如果我們的頁面不斷的增加就要不斷的給webpack.config.js新增外掛和邏輯。所以為了方便和易用性,下一期我們來嘗試寫一些封裝邏輯把構建配置封裝起來,用一個檔案整合常用的配置項統一對工程進行配置。
下一期可能不會很快能更新,一方面因為手上有事情要忙,另一方面整專案我還沒有封裝測試好(這也正是為什麼一直沒有給出github的原因),所以請有關注本系列的同學可能要等等了,有需要的同學也可以關注我的賬號留意更新。
下一期已更新:從搭建vue-腳手架到掌握webpack配置(四.自動化封裝)