webpack 構建前端項目(2)

上一篇講了一些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

基礎配置文件base.conf.js

基礎配置配置了開發和生產通用的配置文件以下: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

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
複製代碼

生產配置文件prod.conf.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 複製代碼
  • webpack-merge 對webpack配置文件進行合併
  • mini-css-extract-plugin:將style中的樣式提取出來做爲單獨的文件。
  • webpack.DefinePlugin 定義全局變量,在項目中能夠直接使用
  • optimization.splitChunks 對代碼進行拆分,
  • terser-webpack-plugin 對js代碼進行壓縮
  • optimize-css-assets-plugin 對css代碼進行壓縮
  • uglifyJs-webpack-plugin 對js進行優化 自動清理項目中的console.log等打印信息


使用postcss自動添加前綴,轉px爲rem

命令行安裝

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文件

將一些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文件路徑
            }
          }
        ]
      }複製代碼

新建build.js

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 便可看見打包後的文件


開發配置文件dev.conf.js

設置 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複製代碼

開啓服務 dev服務

在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複製代碼

  •  express 構建本地服務器
  • webpack-dev-middleware:webpack服務中間件 
  • webpack-dev-middleware:webpack熱服務中間件,項目文件更改時,不刷新頁面,自動替換更改的內容,需在pretreat.js 將entry設置成:
  • entry[moduleName]=isDev?['webpack-hot-middleware/client?noInfo=true&reload=true',res(`src/modules/${moduleName}/main.js`)]:res(`src/modules/${moduleName}/main.js`)複製代碼

  • 在dev.conf.js中加入插件
  • new webpack.HotModuleReplacementPlugin()複製代碼

  • open:在瀏覽器中打開網址

在pakage.json scripts中添加

"dev": "set NODE_ENV='development' && node build/dev.js --open --module"複製代碼

在命令行輸入 npm run dev home 便可開啓dev服務

相關文章
相關標籤/搜索