上一篇講了一些webpack基礎的配置,從這節開始正式構建項目,拿來作演示的是一個移動端項目,淘寶上買的設計圖,計劃作成一個多模塊,模塊單獨創建和維護,模塊間的升級和改造互不影響,但保持項目代碼風格、UI風格的統一。css
根據生產環境和開發環境的差別,須要將webpack的配置文件分紅基礎配置文件、開發配置文件和生產配置文件。刪除 webpack.config.js文件 新建build目錄,在下面創建base.conf.js、dev.conf.js、prod.conf.js,param.js,pretreat.js、build.js、dev.js。html
在src目錄下新建modules文件夾做爲各個模塊的目錄文件夾,新建一個src/modules/home文件夾做爲home模塊,入口文件爲:src/modules/home/main.jsvue
新建一個param.js文件,裏面用存放命令行參數、NODE_ENV;用到了minimist。node
npm i minimist -D複製代碼
const param=require('minimist')(process.argv.slice(2))
module.exports={
mock:param.mock?true:false,
moduleName:param.module==true?'':param.module,
isDev:process.env.NODE_ENV==='development',
openB:param.open || false
}複製代碼
在package.json script中配置命令jquery
"dev:mock": "set NODE_ENV='development' && node build/dev.js --mock --module"複製代碼
而後運行 npm run dev:mock home; minimist就會接受到 {mock:true, module:home} 這樣的參數。webpack
新建一個pretreat.js文件,根據命令行參數生成 webpack配置文件es6
const {moduleName,isDev}=require('./param')
const copyPlugin=require('copy-webpack-plugin')
const fs=require('fs')
const path=require('path')
const chalk=require('chalk')
const res=p=>path.join(process.cwd(),p)
let Err={msg:''}
Object.defineProperty(Err,'msg',{
set(val){
if(val!==''){ //異常處理
console.log(chalk.red(val+'\n'))
process.exit(1)
}
}})
const pretreatConfig=()=>{
Err.msg=moduleName?'':'please input a module name' //判斷是否輸入模塊名
const exit=fs.existsSync(res(`src/modules/${moduleName}`)) //判斷模塊是否存在
Err.msg=exit?'':`module ${moduleName} not found`
let entry={}
entry[moduleName]=res(`src/modules/${moduleName}/main.js`) //入口文件
return{
entry,
output:{
filename:'js/index.js',
path:res(`dist/${moduleName}`),
publicPath:'/'
},
plugins:[
new copyPlugin([
{
from:res( 'public'),
to: res(`dist/${moduleName}`),
ignore: ['.*']
}
])
]
}
}
module.exports=pretreatConfig()複製代碼
用到了copy-webpack-plugin 能夠把制定目錄的文件複製進打包輸出的目錄web
npm i copy-webpack-plugin -D複製代碼
根據打包的模塊名配置了入口文件 出口文件 以及須要複製的文件輸出的位置。express
基礎配置配置了開發和生產通用的配置文件以下:npm
const {moduleName}=require('./param')
const vuePlugin=require('vue-loader/lib/plugin')
const htmlPlugin=require('html-webpack-plugin')
const merge=require('webpack-merge')
const pretreat=require('./pretreat')
const path=require('path')
const res=p=>path.join(process.cwd(),p)
module.exports=merge(pretreat,{
module:{
rules:[
{
test:/\.(jpe?g|gif|png|svg)(\?.*)?$/,
use:[
{
loader:'url-loader',
options:{
limit:10000,
filename:'[name].[hash:5].[ext]',
outputPath:'imgs/'
}
}
]
},
{
test:/\.js$/,
use:[
'babel-loader?cacheDirectory',
{
loader:'eslint-loader',
options:{
fix:true
}
}
]
},
{
test:/\.vue$/,
use:'vue-loader'
}
]
},
plugins:[
new vuePlugin(),
new htmlPlugin({
template:res('template/template.html'),
title:'',
filename:`${moduleName}.html`,
minify:{
collapseWhitespace:true,
}
})
],
resolve:{
extensions:['.js','.vue','.json'],
alias:{ //別名設置
'@':res('src'),
'@style':res('src/common/style'),
'@js':res('src/common/js'),
'@mock':res('mock'),
'@component':res('src/component')
}
},
externals:{
}
})複製代碼
新建一個tempalte/tempalte.html 文件 做爲html-wenbpack-plugin的模板文件
eslint,用於自動檢測代碼風格及模塊引用錯誤,參考了小諾哥的作法,在命令行輸入
npm i eslint eslint-loader babel-eslint standard -D複製代碼
在根目錄增長一個 .eslintrc.js文件
module.exports = {
"parser": "babel-eslint",
"parserOptions": {
"ecmaVersion": 6
},
"env": {
"es6": true,
"browser": true,
"node": true,
"commonjs": false,
"mocha": true,
"jquery": true,
},
"globals": { //全局變量檢測
"globalVar1": true,
"globalVar2": false,
"IS_DEV":false,
"MOCK":false,
"App":true
},
"rules": {
"camelcase":0, //關閉駝峯式寫法檢測
"eqeqeq": "warn",
"curly": 2,
"quotes": ["error", "double"],
"one-var": ["error", {
"var": "always",
"let": "never",
"const": "never"
}],
},
"settings": {
"sharedData": "Hello"
},
"root": true,
"extends": [
"standard"
]
}複製代碼
增長一個 .eslintignore文件
/dist/
/node_modules/
/*.js
複製代碼
設置mode:'production'
const conf=require('./base.conf')
const {CleanWebpackPlugin}=require('clean-webpack-plugin')
const merge=require('webpack-merge')
const miniCss=require('mini-css-extract-plugin')
const terser=require('terser-webpack-plugin')
const optimizeCss=require('optimize-css-assets-webpack-plugin')
const webpack=require('webpack')
const uglifyJs=require('uglifyjs-webpack-plugin')
const path=require('path')
const res=p=>path.join(process.cwd(),p)
module.exports=merge(conf,{
mode:'production',
module:{
rules:[
{
test:/\.(le|c)ss$/,
use:[
miniCss.loader,
'css-loader',
{
loader:'postcss-loader',
options:{
ident:'postcss'
}
},
'less-loader',
{
loader:'style-resources-loader',
options:{
preProcessor:'less',
patterns:[res('src/common/style/fn.less')]
}
}
]
}
]
},
plugins:[
new CleanWebpackPlugin(),
new miniCss({
filename:'css/[name].[hash:5].css',
chunkFilename:'css/[id].[hash:5].css'
}),
new webpack.DefinePlugin({ //定義項目全局變量
MOCK:false,
IS_DEV:false
})
],
optimization:{
minimizer:[
new terser({
parallel:true,
cache:true,
}),
new optimizeCss({}),
new uglifyJs({
exclude:/\/mock/, //忽略 mock文件夾的打包
uglifyOptions:{
compress:{
drop_debugger:true,
drop_console:true //清理console.log
}
}
})
],
splitChunks: { //代碼拆分
chunks: 'async',
minSize: 30000,
maxSize: 0,
minChunks: 1,
maxAsyncRequests: 5,
maxInitialRequests: 1,
automaticNameDelimiter: '~',
automaticNameMaxLength: 30,
name: true,
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10,
name:'vendor'
}
}
}
}
})複製代碼
npm i webpack-merge mini-css-extract-plugin terser-webpack-plugin optimize-css-saaets-plugin uglifyJs-webpack-plugin -D 複製代碼
命令行安裝
npm i postcss-loader postcss-import autoprefixer postcss-pxtorem -D複製代碼
在根目錄新建一個.postcssrc.js文件,rootValue 項目裏的100px 將轉換成 1rem
module.exports = {
plugins:{
'autoprefixer':{},
'postcss-pxtorem':{
rootValue:100,
propList:['*']
}
}}複製代碼
template/template.html 後面加上如下代碼
<script>
(function (doc, win) {
var docEl = doc.documentElement
var resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize'
function recalc() {
var designWidth = 750 //設計稿寬度
var clientWidth = docEl.clientWidth
if (!clientWidth) return
if(clientWidth > designWidth){ //限定縮放最大比例 750px
docEl.style.fontSize='100px'
}else{
docEl.style.fontSize = (100 * clientWidth / designWidth) + 'px' //設置html字體大小
}
}
if (!doc.addEventListener) return
win.addEventListener(resizeEvt, recalc, false)
doc.addEventListener('DOMContentLoaded', recalc, false)})
(document, window)
</script>複製代碼
designWidth 爲設計稿的寬度
在packge.json裏面加入
"browserslist": [
"> 0.2%",
"last 3 versions"
]複製代碼
將一些less共公的參數、變量、mixin提取出來做爲一個單獨的文件,自動注入到每個.vue中,用到了style-resources-loader
npm i style-resources-loader複製代碼
{
test:/\.(le|c)ss$/,
use:[
miniCss.loader,
'css-loader',
{
loader:'postcss-loader',
options:{
ident:'postcss'
}
},
'less-loader',
{
loader:'style-resources-loader',
options:{
preProcessor:'less',
patterns:[res('src/common/style/fn.less')] //less文件路徑
}
}
]
}複製代碼
const webpack=require('webpack')
const conf=require('./prod.conf')
webpack(conf,(err,status)=>{
if(err){ throw err }
process.stdout.write(str.toString({
colors:true,
modules:false,
children:false,
chunks:false,
chunkModules:false
}))
})複製代碼
使用webpack()打包項目,package.json的script添加:
"build": "set NODE_ENV='production' && node build/build.js --module",複製代碼
命令行輸入npm run build home 便可看見打包後的文件
設置 mode:'development'
const merge=require('webpack-merge')
const conf=require('./base.conf')
const vConsole=require('vconsole-webpack-plugin')
const webpack=require('webpack')
const {mock}=require('./param')
module.exports=merge(conf,{
mode:'development',
devtool:'inline-sourceMap',
module:{
rules:[
{
test:/\.(le|c)ss$/,
use:[
'style-loader',
{
loader:'css-loader',
options:{
sourceMap:true,
}
},
{
loader:'postcss-loader',
options:{
ident:'postcss',
sourceMap:true,
}
},
{
loader:'less-loader',
options:{
sourceMap:true
}
},
{
loader:'style-resources-loader',
options:{
preProcessor:'less',
patterns:[res('src/common/style/fn.less')]
}
}
]
}
]
},
plugins:[
new vConsole({
enable:true
}),
new webpack.HotModuleReplacementPlugin(),
new webpack.DefinePlugin({
MOCK:mock,
IS_DEV:true
})
]})複製代碼
設置devtool爲’inline-sourceMap'開啓js文件跟蹤,
css-loader postcss-loader less-loader 設置 options 的sourceMap爲true 跟蹤樣式文件。
安裝 vconsole-webpack-plugin ,用於在頁面調起控制檯,適合手機調試
npm i vconsole-webpack-plugin複製代碼
在config目錄下新建一個local文件,裏面寫入模塊的端口號,
module.exports = {
router: {
login: 8080,
home: 8081,
car: 8082
}}複製代碼
在dev.js中寫入
const conf=require('./dev.conf')
const devMiddleware=require('webpack-dev-middleware')
const hotMiddleware=require('webpack-hot-middleware')
const express=require('express')
const app=new express()
const webpack=require('webpack')
const open=require('open')
const {mock,moduleName,openB}=require('./param')
const port=require('../config/local.js').router
const path=require('path')
const res=p=>path.join(process.cwd(),p)
const compiler=webpack(conf)
app.use(devMiddleware(compiler,{
publicPath:conf.output.publicPath,
quiet:true
}))
app.use(hotMiddleware(compiler,{
log:false,
heartbeat:1000
}))
if(mock){
app.use(express.static(res('mock/assets')))
}
app.listen(port[moduleName],()=>{
console.log(`server running at http://localhost:${port[moduleName]}`)
if(openB){
open(`http://localhost:${port[moduleName]}/${moduleName}.html/#/`)
}})複製代碼
用到了webpack-dev-middleware webpack-hot-middleware express open
npm i webpack-dev-middleware webpack-hot-middle-ware express open -D複製代碼
entry[moduleName]=isDev?['webpack-hot-middleware/client?noInfo=true&reload=true',res(`src/modules/${moduleName}/main.js`)]:res(`src/modules/${moduleName}/main.js`)複製代碼
new webpack.HotModuleReplacementPlugin()複製代碼
在pakage.json scripts中添加
"dev": "set NODE_ENV='development' && node build/dev.js --open --module"複製代碼
在命令行輸入 npm run dev home 便可開啓dev服務