上一篇讲了一些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服务