手把手教你webpack4.x从零开始搭建vue项目

1.首先安装各个模块所须要的依赖

  • npm init -y  初始化package.json文件 。

     如下全部安装命令的 ‘-D‘  表明安装在该文件的devDependencies中
     npm的文档说明:dependencies是运行时依赖,devDependencies是开发时的依赖
css

  • npm i webpack webpack-cli webpack-dev-server webpack-merge -D

      webpack能够看作是模块打包机。webpack-cli封装了与CLI处理相关的全部代码。它捕获选项并将它们发送到webpack编译器。webpack-dev-server是webpack官方提供的一个小型express服务器, 为webpack打包生成的资源文件提供web服务。webpack-merge是和用来区分两个不一样的环境html

  • npm i cross-env -D

      能够跨平台设置环境变量vue

  • npm i @babel/core babel-loader @babel/preset-env -D

      @babel/core是转译器自己,提供了babel的转译API。babel-loader就是调用这些API来完成转译js过程的。Babel 7宣布废弃babel-preset-es201x而采用新的env插件node

  • npm i  @babel/polyfill @babel/runtime-corejs2 @babel/plugin-transform-runtime -D

      Babel默认只转换新的JavaScript句法,而不转换新的API。须要在入口加载@babel/polyfill插件来转换,能够解决某些方法在IE的兼容性问题(这种方式是经过向全局对象和内置对象的prototype上添加方法实现的,会形成全局变量污染),在babel7.x版本中使用 :@babel/runtime-corejs2,能够结合@babel/plugin-transform-runtime, 可避免全局污染。webpack

      在转换 ES2015 语法为 ECMAScript 5 的语法时,babel 会须要一些辅助函数,例如 _extend。babel 默认会将这些辅助函数内联到每个 js 文件里,这样文件多的时候,项目就会很大。因此 babel 提供了 transform-runtime 来将这些辅助函数“搬”到一个单独的模块 babel-runtime 中,这样作能减少项目文件的大小。web

  • npm i @babel/plugin-proposal-decorators -D

      将类和对象装饰器编译为ES5express

  • npm i @babel/plugin-syntax-dynamic-import -D (在babel7.x中彷佛并不须要再进行单独安装)

     容许解析import() 延迟加载,懒加载
npm

  • npm i @babel/plugin-proposal-optional-chaining -D

     能够访问深层嵌套的属性,能够不用逻辑与去判断    // const baz = obj?.foo?.bar?.baz
json

注意: babel7.0后,已经再也不使用@babel/preset-stage-x的预设了, 所需的插件都须要单独去安装配置,根据公司的项目须要,能够选择性的去配置 :@babel/plugin-***    这里不作过多赘述。api

  • npm i vue-loader vue-template-compiler -D

       解析vue文件须要安装这两个插件

  • npm i uglifyjs-webpack-plugin -D

       用来在生产环境压缩js

  • npm i css-loader sass node-sass sass-loader postcss-loader autoprefixer mini-css-extract-plugin optimize-css-assets-webpack-plugin -D

       css-loader处理css,用来解析@import这种语法。sass-loader将sass语言转换成css, postcss-loader 和 autoprefixer用来为css的样式自动添加浏览器前缀,mini-css-extract-plugin是提取css文件,再也不以style标签存放,而是建立link标签引入,因此再也不须要style-loader。optimize-css-assets-webpack-plugin用来压缩css

  • npm i url-loader file-loader -D

       url-loader打包图片资源,file-loader打包字体等资源

  • npm i html-webpack-plugin clean-webpack-plugin copy-webpack-plugin -D

       html-webpack-plugin用来打包时自动生成html文件,clean-webpack-plugin打包时会自动清除dist,copy-webpack-plugin用来拷贝静态资源到打包目录

  • npx eslint --init, npm i babel-eslint -D

npx eslint --init 一键配置eslint规范。此外须要安装babel-eslint工具。建立.eslintignore文件可设置忽略检测的文件。在webstorm工具中设置eslint,编译器能够实时检测,显示错误

安装完上述依赖以后,须要根据本身的安装去配置.babelrc(babel的配置文件)和postcss.config.js。这里直接附上我本身的配置仅供参考。以下:

  • .babelrc

{
  "presets": [
    "@babel/preset-env"
  ],
  "plugins": [
    ["@babel/plugin-transform-runtime", {
      "corejs": 2
    }],
    [
      "@babel/plugin-proposal-decorators",
      {
        "legacy": true
      }
    ],
    "@babel/plugin-proposal-optional-chaining"
  ]
}
复制代码

  • postcss.config.js

module.exports = {  
  plugins: [
    require('autoprefixer')  
  ]
}复制代码

2. webpack公共文件配置 :webpack.base.config.js

let path = require('path')
let MiniCssExtractPlugin = require('mini-css-extract-plugin')   // 提取css公共文件
let { VueLoaderPlugin } = require('vue-loader')     //vue-loader  15.x版本之后须要引入
let HtmlWebpackPlugin = require('html-webpack-plugin')    //自动生成html
let CopyWebpackPlugin = require('copy-webpack-plugin')  //拷贝静态资源
let { CleanWebpackPlugin } = require('clean-webpack-plugin')  //打包前清除dist
let webpack = require('webpack')

module.exports = {
  entry: [ path.resolve(__dirname, '../src/main.js') ],
  output: {
    filename: 'js/[name].[hash:8].js',   //打包后的js文件放在js目录下,添加hash值防止缓存
    chunkFilename: 'js/[name].[hash:8].js', // 配合按需加载路由来使用,用来修改打包后的各个JS模块文件名字,具体请看下方打包后的截图
    path: path.resolve(__dirname, '../dist'), //输出的目录
    publicPath: '/'     //静态资源cdn的地址
  },
  stats: {     // 本地起服务或者打包时候,清除过多的日志信息,精简控制台信息。
    modules: false,
    children: false,
    chunks: false,
    chunkModules: false,
    assets: false,  //不显示资源打包信息
  },
  module: {   //转换规则
    rules: [
      {
        test: /\.vue$/,
        exclude: /node_modules/,
        use: ['vue-loader']
      },
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: ['babel-loader']
      },
      {
        test: /\.(css|scss|sass)$/,   
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          'postcss-loader',
          'sass-loader'
        ]
      },
      {
        test: /\.(png|jpe?g|svg|gif)$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 20 * 1024   // 不设置这个的话,打包后的图片默认是超过8k时,会以base64编码
              name: 'images/[name].[hash:8].[ext]'
            }
          }
        ]
      },
      {
        test:/\.(woff|woff2|eot|ttf|otf)$/,
        use: [
          {
            loader: 'file-loader',
            options: {
              name: 'fonts/[name].[hash:8].[ext]'
            }
          }
        ]
      },
    ]
  },
  resolve: {
    extensions: ['.vue', '.js', '.scss',  '.json'],// 可以使用户在引入模块时不带扩展名字, 自动解析
    alias: {    //别名,方便快速查找模块
      '@': path.resolve(__dirname, '../src')
    }
  },
  plugins: [
    new VueLoaderPlugin(),
    new HtmlWebpackPlugin({
      filename: 'index.html',
      template: path.resolve(__dirname, '../index.html'),
      title: '这是自定义的名字,请随意',
      favicon: path.resolve(__dirname, '../src/images/logon.png'),  //浏览器标题的图标
      hash: true,
      minify: {
        removeAttributeQuotes: true,    //删除属性的引号
        collapseWhitespace: true    //删除空白符与折叠行
      }
    }),
    new CleanWebpackPlugin(),
    new CopyWebpackPlugin([
      {
        from: path.resolve(__dirname, '../src/assets'),
        to: path.resolve(__dirname, '../dist/copyAssets')
      }
    ]),
    new MiniCssExtractPlugin({
      filename: 'css/[name].[hash:8].css'
    }),
    new webpack.ProgressPlugin()      //显示打包进度的插件
  ]
}
复制代码

3.webpack开发环境配置 :webpack.dev.config.js

let webpack = require('webpack') 
let path = require('path') 
let merge = require('webpack-merge')     
let base = require('./webpack.base.config')

module.exports = merge(base, {
  mode: 'development',     // 定义环境变量
  devtool: 'cheap-module-eval-source-map',
  devServer: {
    port: 8000,     //设置端口
    progress: true,// 控制台显示百分比,HDM进度
    hot: true,     // 启用热更新,控制台所有刷新
    open: true,    // 自动打开浏览器
    historyApiFallback: true,
    contentBase: path.resolve(__dirname, '../dist'),
    proxy: {
      '/api': {        //url中匹配到'/api', 就会把'/api'以前的东西所有替换成target
        target: '',      // 目标服务器host
        changeOrigin: true,   //  表示要改变原始host
        secure: false,   // 默认请求的服务是https的, 而且证书是未认证的,因此须要关闭安全检测。
        clentLogLevel: 'none',  //当使用内联模式(inline mode)时,会在开发工具(DevTools)的控制台(console)显示消息,例如:在从新加载以前,在一个错误以前,或者 模块热替换(Hot Module Replacement) 启用时。默认值是 info。
        pathRewrite: {
          '^/api': ''   // 重写请求,源访问地址中包含'/api'的将会替换为空
        }
      }
    }
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin(),   //热更新插件
    new webpack.NamedChunksPlugin()    // 使用此插件热更新时控制台会显示模块的相对路径
  ]
})复制代码

4.webpack生产环境配置 :webpack.prod.config.js

let merge = require('webpack-merge')
let base = require('./webpack.base.config')
let UglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin')
let OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin')

module.exports = merge(base, {
  mode: 'production',
  devtool: 'cheap-module-source-map',
  optimization: {
    minimizer: [
      new UglifyjsWebpackPlugin({   // 生产环境压缩JS
        cache: true,    //是否否启用缓存
        parallel: true,   //多通道并行处理
        sourceMap: false, //生产环境关闭源码映射
        uglifyOptions: {
          warnings: false,    //清除警告
          compress:{
            drop_debugger: true,	// 清除degugger
            drop_console: true   //清除全部的console信息
          }
        }
      }),
      new OptimizeCssAssetsPlugin()   // 生产环境压缩css
    ],
    splitChunks: {   //用于拆分代码,找到 chunk 中共同依赖的模块进行“提取”和“分离”到单独的文件中,减小打包后体积,能够避免内存溢出的问题。
      chunks: 'all'
    }
  },
   performance: {                       //  webpack 的性能提示
     hints: 'warning',                 // 显示警告信息
     maxEntrypointSize: 5 * 1024 * 1024,    // 设置入口文件的最大致积为5M  (以字节为单位)
     maxAssetSize: 20* 1024 * 1024,        // 设置输出文件的最大致积为20M  (以字节为单位)
     assetFilter (assetFilename) {        // 提供资源文件名的断言函数
      return assetFilename.endsWith('.js') || assetFilename.endsWith('.css')
     }
    }
})
复制代码

4.最后一步

在package.json文件的scripts中添加以下,而后执行npm run build便可打包

"serve": "cross-env NODE_ENV=development webpack-dev-server --config ./build/webpack.dev.config.js",
"build": "cross-env NODE_ENV=production webpack --config ./build/webpack.prod.config.js"
复制代码

5.总结

       以上配置是采用webpack4.x+babel7.x版本,自行配置时请注意版本兼容问题。若是您想要升级项目到babel7.0, 文档中也提供了一个升级工具:执行 npx babel-upgrade--write--install一键安装babel7.0所须要的全部配置,而且会自动将配置写入package.json和.babelrc文件中。详情请参考文档说明。如下是打包后的截图:


copyAssets是拷贝的静态资源。为何打包后的js会有0**,2** 这么几个文件呢?这是由于我vue的路由使用了按需加载,每一个页面都生成本身单独的js。等须要的时候,才会去加载这些js的。可是这个0, 1, 2,...等等的文件名字默认是以id命名的,这看起来不友好,并且不太方便排错,因此须要在引入路由时候,须要使用特殊的注释语法来提供webpackChunkName,配合output里面设置的chunkFilename: 'js/[name].[hash:8].js' 来修改文件名,代码以下:

export default new Router({
  mode: 'history',
  routes: [
    {
      path: '',
      redirect: '/Home'
    },
    {
      path: '/Home',
      name: 'Home',
      component: () => import(/* webpackChunkName: 'Home' */ '@/view/Home')
    },
    {
      path: '/Hello',
      name: 'Hello',
      component: () => import(/* webpackChunkName: 'Hello' */ '@/view/Hello')
    }
  ]
})复制代码

最终打包后的结果以下,看看,这就舒服多了~