kd-cloud-web / Blog

一群人, 关于前端, 做一些有趣的事儿
13 stars 1 forks source link

webpack4工程化搭建 #50

Open 1h1h opened 4 years ago

1h1h commented 4 years ago

webpack4 配置

从零搭建webpack工程化项目

1.webpack简介

https://upload-images.jianshu.io/upload_images/143845-814fabf25c4788e8.png?imageMogr2/auto-orient/strip|imageView2/2/format/webp

2. webpack 、gulp区别

1.webpack: 基于模块化打包工具

2.gulp:基于任务流的构建工具

gulp.task('watch', function(cb) {
  runSequence(
    ['copy:img', 'sass', 'include', 'copy:js'],
    'dev',
    cb
  );
});

gulp.task('start', ['clean:dist', 'clean:tmp'], plugins.shell.task([
  'concurrently "gulp watch" "npm start"'
]));

gulp.task('debug', ['clean:dist', 'clean:tmp'],plugins.shell.task([
  'concurrently "gulp watch" "npm run debug"'
]));

gulp.task('build', function(cb) {
  runSequence(
    ['clean:dist', 'clean:tmp'],
    'build:img',
    ['build:css', 'build:js'],
    'build:html',
    'clean:tmp',
    cb
  );
});

3.核心配置

1 entry(入口)

设置文件入口,单文件或者多文件

// entry 单个文件入口
const path = require('path')
module.export = {
    entry: './src/main.js'
}

// 多个文件入口,采用对象形式
entry: {
    index: path.join(__dirname, 'src/main.js'),
    more: path.join(__dirname, 'src/more.js')
}

2. output(出口)

output: {
    // 输出名字
    filename: 'js/[name].[hash].js', // 采用哈希
    path: path.join(__dirname, 'dist')
}

3 loader

  1. loader是让webpack能够处理非js文件,将其他类型转换为webpack可以处理的有效模块
  2. loader有rules的两个属性
    • test: 正则匹配哪些文件
    • use:使用哪些loader
module: {
    rules: [
        {
            test: /\.less$/,
            use: [
              {loader: 'style-loader'},
              {loader: 'css-loader'},
              {loader: 'less-loader'}
            ]
         }
    ]
}

4 plugins(插件)

  1. 由于插件可以携带参数/选项,你必须在 webpack 配置中,向 plugins 属性传入 new 实例。
 // 插件
  plugins: [
    new HtmlWebpackPlugin({
      template: path.join(__dirname, 'index.html')
    }),
    // 每次构建前清除dist文件
    new CleanWebpackPlugin()
  ]

4 搭建webpack基本框架

1.项目初始化(webpack-demo)

image-20200526190911811

2.yarn init

3.安装webpack包

yarn add webpack webpack-cli webpack-dev-server webpack-merge --dev

webpack-merge: 可用于合并公共配置和开发配置或者生产配置

4修改package.json,启动脚手架命令 yarn dev || yarn build

"scripts": {
    "dev": "webpack-dev-server --config webpack.dev.js --mode development",
    "build": "webpack --config webpack.prod.js --mode production"
  },

5.webpack.base.js 中为配置公共部分

  1. 其中require引用,loader引用的需要执行安装·yarn add xxx --dev

  2. 用到plugins

    html-webpack-plugin: 将打包完成的文件内联到生成的HTML文件中

    clean-webpack-plugin: 每次执行是都清除dist文件

  3. 安装yarn add babel-loader@7.1.1 babel-code --dev存在版本问题

const path = require('path');
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const {CleanWebpackPlugin} = require('clean-webpack-plugin') // 对象
module.exports = {
  // 入口
  entry: {
    index: path.join(__dirname, 'src/main.js'),
    more: path.join(__dirname, 'src/more.js')
  },

  // 出口
  output: {
    filename: 'js/[name].[hash].js',
    path: path.join(__dirname, 'dist')
  },
  // loader 
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          {loader: 'style-loader'},
          {loader: 'css-loader'},
        ],
      },
      // 配置less
      {
        test: /\.less$/,
        use: [
          {loader: 'style-loader'},
          {loader: 'css-loader'},
          {loader: 'less-loader'}
        ]
      },
        {
          test: /\.js$/,
          loader: 'babel-loader',
          include: [resolve('src')],
        },
        {
          test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
          loader: 'url-loader',
          options: {
            limit: 10000,
            name: utils.assetsPath('img/[name].[hash:7].[ext]'),
          },
        },
    ]
  },
  // 插件
  plugins: [
    new HtmlWebpackPlugin({
      template: path.join(__dirname, 'index.html')
    }),
    // 每次构建前清除dist文件
    new CleanWebpackPlugin()
  ]
}

6.webpack.prod.js生产版本配置

  1. webpack.base.js合并过来
const merge = require("webpack-merge")
const common = require('./webpack.base.js') // 引入公有模块
module.exports = merge(common, {
  mode: "production"
})

7.webpack.dev.js开发版本配置

  1. webpack.base.js合并过来
  2. 使用devServer配置端口,加载目录,以及热更新
  3. 使用热更新插件webpack.HotModuleReplacementPlugin
const merge = require('webpack-merge') // 引入webpack-merge功能模块
const common = require('./webpack.base.js') // 引入公有模块
const webpack = require('webpack')
// 由于许多不同环境,我们需要抽离公共部分
module.exports = merge(common, {
  devServer: {
    contentBase: './dist', // 本地服务器加载文件的目录
    port: '8899', // 设置端口号
    inline: true, // 文件修改后时时刷新
    historyApiFallback: true, // 不跳转
    hot: true // 热更新
  },
  // 热更新插件
  plugins: [
    new webpack.HotModuleReplacementPlugin({
      multiStep: true
    })
  ],
  mode: "development"
})

这样,一个基本的框架就完成。但是这远远不够,还没有加入现在流行的单页面框架react,vue,接下来,就采用vue来搭建单页面

5添加vue完成搭建

1.安装vue依赖

yarn add vue vue-loader vue-template-compiler --dev

2整改原先项目

  1. main.js中引入vue

    import Vue from 'vue';
    import APP from './App.vue'; // 引入App.vue
    import router from './router';
    new Vue({
     el: '#app',
     router,
     // store,
     render: h => h(App)
    })
  2. 在index.html修改添加id = app

    <div id="app"></div> // 解决找不到Cannot find element: #app
  3. 添加App.vue

  4. 添加router.js

    直接使用component:import('xx')会报错,Unexpected token,

    可以采用 r => require.ensure( [], () => r (require('../src/component/home.vue')))

    或者 yarn add babel-plugin-syntax-dynamic-import --dev,

    修改webpack.base.js

    {
       test: /\.js$/,
           loader: 'babel-loader',
            // include: [path.join(__dirname, 'src')],
           exclude : /node_modules/,
              options: {
                  plugins: ['syntax-dynamic-import']
            },
    },

    router.js代码如下:

    import Vue from 'vue'
    import Router from 'vue-router';
    // import Home from '../src/component/home.vue'
    //const Home = r => require.ensure( [], () => r (require('../src/component/home.vue')))
    Vue.use(Router)
    
    const router = new Router({
     mode: 'history',
     routes: [
       {
         path: '/',
         name: 'home',
         component: () => import('../src/component/home.vue')
       }
     ]
    })
    
    router.beforeEach((to, from, next) => {
     next()
    })
    
    export default router;

    这样就完成了基本VUE项目搭建,主要在webpack.base.js

    const path = require('path');
    const webpack = require('webpack')
    const HtmlWebpackPlugin = require('html-webpack-plugin')
    const {CleanWebpackPlugin} = require('clean-webpack-plugin')
    const VueLoaderPlugin = require('vue-loader/lib/plugin.js')
    module.exports = {
     // 入口
     entry: {
       index: path.join(__dirname, 'src/main.js'),
       // more: path.join(__dirname, 'src/more.js')
     },
     resolve: {
       alias: {
         'vue$': 'vue/dist/vue.esm.js',
         '@': path.resolve(__dirname, '../src')
       },
     },
     // loader 
     module: {
       rules: [
         {
           test: /\.css$/,
           use: [
             {loader: 'style-loader'},
             {loader: 'css-loader'},
           ],
         },
         // 配置less
         {
           test: /\.less$/,
           use: [
             {loader: 'style-loader'},
             {loader: 'css-loader'},
             {loader: 'less-loader'}
           ]
         },
         {
           test: /\.js$/,
           loader: 'babel-loader',
           // include: [path.join(__dirname, 'src')],
           exclude : /node_modules/,
           options: {
             plugins: ['syntax-dynamic-import']
           },
         },
         {
           test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
           loader: 'url-loader',
           options: {
             limit: 10000,
             name: 'img/[name].[hash:7].[ext]',
             outputPath: path.join(__dirname, 'dist/')
           },
         },
         {
           test: /\.(eot|woff|woff2|ttf)$/,
           loader: 'file-loader',
           options: {
             name: '[name].[ext]?[hash]'
           }
         },
         {
           test: /\.vue$/,
           use: ['vue-loader']
         }
       ]
     },
     // 插件
     plugins: [
       new VueLoaderPlugin(),
       new HtmlWebpackPlugin({
         template: path.join(__dirname, 'index.html')
       }),
       // 每次构建前清除dist文件
       new CleanWebpackPlugin(),
     ],
     // 出口
     output: {
       filename: 'js/[name].[hash].js',
       path: path.join(__dirname, 'dist')
     },
    }

6 打包优化

1 分离CSS文件

安装yarn add mini-css-extract-plugin --dev

style-loader替换成MiniCssExtractPlugin.loader

{
        test: /\.(le|c)ss$/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
            options: {
              publicPath: '../'
            }
          },
          'css-loader',
          'less-loader'
        ]
},
plugins: [
    // 输出
    new MiniCssExtractPlugin({
        filename: "css/[name].[contenthash:8].css",
        chunkFilename: "css/[id].[contenthash:8].css"
    })
]

2 消除冗余css

安装:yarn add purifycss-webpack purify-css glob --dev不太可靠

3 压缩CSS代码

安装yarn add optimize-css-assets-webpack-plugin --dev

// 压缩CSS代码
const optimizeCssAssetsWebpack = require('optimize-css-assets-webpack-plugin')
optimization: {
    minimizer: [
      new optimizeCssAssetsWebpack({})
    ]
  }

4 压缩JS代码

安装: yarn add terser-webpack-plugin --dev

const terserWebpackPlugin = require('terser-webpack-plugin')
new terserWebpackPlugin({
    parallel: 2, // 开启几个进程来处理压缩,
    cache: true
})

5 分离不常用的第三方插件

optimization: {
    // 分离chunks,不必要第三方
    splitChunks: {
    chunks: "all",
      minSize: 30000,
      maxSize: 0,
      minChunks: 1,
      maxAsyncRequests: 5,
      maxInitialRequests: 3,
      automaticNameDelimiter: "-", // chunk分隔符
      name: true,
      cacheGroups: {
        // 设置缓存组用来抽取满足不同规则的chunk
        element: {
          name: "chunk-element-ui",
          priority: -9,
          chunks: "all",
          test: /[\\/]node_modules[\\/](element-ui)/,
          reuseExistingChunk: true, // 重复使用已经存在的块
          enforce: true, // 强制生成
        },
      }
    },

6 gZip 压缩

安装 yarn add compression-webpack-plugin --dev

// 若已经经过图片或者其他压缩的,gzip压缩不明显
plugins: [
    new CompressionPlugin({
      // gzip压缩配置
      test: /\.js$|\.html$|\.css/, // 匹配文件名
      threshold: 10240, // 对超过10kb的数据进行压缩
      deleteOriginalAssets: false, // 是否删除原文件
    }),
  ],