知识前端学习

webpack配置及优化

2019-02-25  本文已影响453人  职场丧尸

https://github.com/Yanlin-Zhu/webpack

webpack安装

安装本地webpack
安装webpack和webpack-cli

//安装前先npm初始化
npm init -y
npm i webpack webpack-cli -D

webpack可以进行0配置(默认只支持打包js文件)

运行npx webpack会进入node_modules/bin/webpack.cmd文件执行

@IF EXIST "%~dp0\node.exe" (
  "%~dp0\node.exe"  "%~dp0\..\webpack\bin\webpack.js" %*
) ELSE (
  @SETLOCAL
  @SET PATHEXT=%PATHEXT:;.JS;=;%
  node  "%~dp0\..\webpack\bin\webpack.js" %*
)

判断是否有node.exe没有则执行上一级webpack文件中的\bin\webpack.js文件

手动配置webpack

// webpack是node写出来的 node的写法
let path = require('path')

module.exports = {
  mode: 'development', // 模式默认有两种production development开发模式代码不压缩看的清晰
  entry: './src/index.js', // 入口文件
  output: {
    filename: '/js/bundle.[hash].js', // 打包后文件名
    path: path.resolve(__dirname, 'dist'), //打包后路径必须是绝对路径resolve方法把相对路径解析成绝对路径,__dirname加不加都可以,它代表在当前目录下产生一个dist目录
    publicPath: 'http://www.weilongyun.com' // 给所有打包文件引入时加前缀,包括css,js,img,如果只想处理图片可以单独在url-loader配置中加publicPath(上传七牛云等cdn加速时可用)
  },
}

三种hash值区别如下
https://www.cnblogs.com/giggle/p/9583940.html

打包后文件分析(可略过)

 (function(modules) { // webpackBootstrap
    // The module cache 先定义一个缓存
    var installedModules = {};

    // The require function 配置实现了require函数
    function __webpack_require__(moduleId) { // 参数"./src/index.js"

        // Check if module is in cache 模块是否在缓存中
        if(installedModules[moduleId]) {
            return installedModules[moduleId].exports;
        }
        // Create a new module (and put it into the cache)
        var module = installedModules[moduleId] = {
            i: moduleId,
            l: false,
            exports: {}
        };

        // Execute the module function 执行传入this指向,模块,模块的空对象exports: {},require方法
        modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);

        // Flag the module as loaded
        module.l = true;

        // Return the exports of the module
        return module.exports;
    }


    // expose the modules object (__webpack_modules__)
    __webpack_require__.m = modules;

    // expose the module cache
    __webpack_require__.c = installedModules;

    // define getter function for harmony exports
    __webpack_require__.d = function(exports, name, getter) {
        if(!__webpack_require__.o(exports, name)) {
            Object.defineProperty(exports, name, { enumerable: true, get: getter });
        }
    };

    // define __esModule on exports
    __webpack_require__.r = function(exports) {
        if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
            Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
        }
        Object.defineProperty(exports, '__esModule', { value: true });
    };

    // create a fake namespace object
    // mode & 1: value is a module id, require it
    // mode & 2: merge all properties of value into the ns
    // mode & 4: return value when already ns object
    // mode & 8|1: behave like require
    __webpack_require__.t = function(value, mode) {
        if(mode & 1) value = __webpack_require__(value);
        if(mode & 8) return value;
        if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
        var ns = Object.create(null);
        __webpack_require__.r(ns);
        Object.defineProperty(ns, 'default', { enumerable: true, value: value });
        if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
        return ns;
    };

    // getDefaultExport function for compatibility with non-harmony modules
    __webpack_require__.n = function(module) {
        var getter = module && module.__esModule ?
            function getDefault() { return module['default']; } :
            function getModuleExports() { return module; };
        __webpack_require__.d(getter, 'a', getter);
        return getter;
    };

    // Object.prototype.hasOwnProperty.call
    __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };

    // __webpack_public_path__
    __webpack_require__.p = "";


    // Load entry module and return exports
    return __webpack_require__(__webpack_require__.s = "./src/index.js"); // 传入入口模块
 })
//  自执行函数传入一个对象即modules
 ({
 "./src/a.js": // key
 (function(module, exports) { // value
  eval("module.exports = 'a_moudle'\n\n//# sourceURL=webpack:///./src/a.js?");
  }),
 "./src/index.js":
 (function(module, exports, __webpack_require__) {
  eval("let str = __webpack_require__(/*! ./a.js */ \"./src/a.js\")\r\n\r\nconsole.log(str)\n\n//# sourceURL=webpack:///./src/index.js?");
 })
});

在package.json中配置运行脚本

"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack --config webpack.config.js"
  },

webpack-dev-server起本地服务

安装
npm i webpack-dev-server -D
devServer: { // 开发服务器的配置(webpack-dev-server)
   port: 3000,
   progress: true,
   contentBase: './dist' // 执行时执行打包后文件,可以不配置
 },

html-webpack-plugin(安装插件打包html)

安装
npm i html-webpack-plugin -D
let HtmlWebpackPlugin = require('html-webpack-plugin')

 plugins: [ // 数组,放着所有webpack插件
    new HtmlWebpackPlugin({ // 用于使用模板打包时生成index.html文件,并且在run dev时会将模板文件也打包到内存中
      template: './index.html', // 模板文件
      filename: 'index.html', // 打包后生成文件
      hash: true, // 添加hash值解决缓存问题
      minify: { // 对打包的html模板进行压缩
        removeAttributeQuotes: true, // 删除属性双引号
        collapseWhitespace: true // 折叠空行变成一行
      }
    })
  ]

css-loader style-loader(使用loader打包css文件)

安装
npm i css-loader style-loader -D
配置
module: { // 模块
    // loader
    rules: [ // 规则 loader的特点-希望单一功能
      // css-loader-解析css文件包括css文件的引入(@import)语法等
      // style-loader-把css文件插入到head标签中
      {
        test: /\.css/,
        // 字符串只用一个loader,多个loader用数组配置
        // loader的顺序默认从右向左执行,从下到上执行
        // 如果需要传参loader还可以配置成对象
        use: ['style-loader', 'css-loader']
      }
    ]
  }

解析less文件配置

安装
npm i less-loader -D
配置
// 可处理less文件除此之外还有sass、stylus
// node-sass、sass-loader、stylus-loader
{
  test: /\.less$/,
  use: [
    'style-loader', // 插入style标签
    'css-loader', // 解析路径
    'less-loader' // 把less转换成css
  ]
}

在只安装了less-loader、css-loader、style-loader时引入less文件报错Cannot find module 'less',此时安装less就好了

npm i less -D

mini-css-extract-plugin(抽离css插件)

安装
npm i mini-css-extract-plugin -D
配置
let MiniCssExtractPlugin = require('mini-css-extract-plugin')

new MiniCssExtractPlugin({ // 抽离css样式
  filename: '/css/main.css'// 抽离出来的文件名
})

{
  test: /\.css$/,
  // 字符串只用一个loader,多个loader用数组配置
  // loader的顺序默认从右向左执行,从下到上执行
  // 如果需要传参loader还可以配置成对象
  use: [
    // 'style-loader',
    MiniCssExtractPlugin.loader, // 将css文件抽离出来,不再以style标签存放,而是创建link标签引入
    'css-loader'
    ]
},
// 可处理less文件除此之外还有sass、stylus
// node-sass、sass-loader、stylus-loader
{
  test: /\.less$/,
  use: [
    // 'style-loader', // 插入style标签
    MiniCssExtractPlugin.loader, // 将less文件抽离出来,不再以style标签存放,而是创建link标签引入
    'css-loader', // 解析路径
    'less-loader' // 把less转换成css
  ]
}

使用mini-css-extract-plugin抽离css插件文件时可使用optimize-css-assets-webpack-plugin优化压缩css以及js文件

optimize-css-assets-webpack-plugin

使用optimize-css-assets-webpack-plugin压缩css文件就必须使用uglifyjs-webpack-plugin压缩js文件
虽然webpack 5可能带有一个内置的CSS最小化器,但对于webpack 4,您需要自带一个CSS最小化器。为了缩小输出,可以使用像optimize-css-assets-webpack-plugin这样的插件。设置优化。最小化器覆盖webpack提供的默认值,所以请确保还指定了JS最小化器:

安装
npm i optimize-css-assets-webpack-plugin uglifyjs-webpack-plugin -D
配置
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");

optimization: { // 优化项
  minimizer: [
    new UglifyJsPlugin({ // 压缩js
      cache: true,
      parallel: true,
      sourceMap: true // set to true if you want JS source maps
    }),
    new OptimizeCSSAssetsPlugin({}) // 压缩css
  ]
},

autoprefixer包(自动给css样式添前缀)该包需要通过postcss-loader处理

安装
npm i postcss-loader autoprefixer -D
webpack.config.js中配置
{
  test: /\.css$/,
  // 字符串只用一个loader,多个loader用数组配置
  // loader的顺序默认从右向左执行,从下到上执行
  // 如果需要传参loader还可以配置成对象
  use: [
    // 'style-loader',
    MiniCssExtractPlugin.loader, // 将css文件抽离出来,不再以style标签存放,而是创建link标签引入
    'css-loader',
    'postcss-loader'
    ]
},
// 可处理less文件除此之外还有sass、stylus
// node-sass、sass-loader、stylus-loader
{
  test: /\.less$/,
  use: [
    // 'style-loader', // 插入style标签
    MiniCssExtractPlugin.loader, // 将less文件抽离出来,不再以style标签存放,而是创建link标签引入
    'css-loader', // 解析路径
    'postcss-loader',
    'less-loader' // 把less转换成css
  ]
}
新建postcss.config.js配置文件
module.exports = {
  plugins: [require('autoprefixer')]
}

js处理

使用babel将ES6或更高级的语法转换成ES5

安装
npm i babel-loader @babel/core  @babel/preset-env -D
配置
{
  test: /\.js$/,
  use: [
    {
        loader: 'babel-loader',
        options: { // 用babel-loader吧es6转换成es5
        presets: [ // 预设规则
          '@babel/preset-env'
        ]
      }
    }
  ]
}

未使用babel前使用es6语法会报错

ERROR in bundle.d92a2720a0d7daf51415.js from UglifyJs
Unexpected token: name (str) [./src/index.js:1,0][bundle.d92a2720a0d7daf51415.js:91,4]

此时写类和装饰器(es7)还是会报错

ERROR in ./src/index.js
Module build failed (from ./node_modules/babel-loader/lib/index.js):
SyntaxError: D:\zhuyanlin\webpack\src\index.js: Support for the experimental syntax 'classProperties' isn't currently enabled (8:5):
安装
npm i @babel/plugin-proposal-decorators @babel/plugin-proposal-class-properties -D
配置
{
  test: /\.js$/,
  use: [
    {
        loader: 'babel-loader',
        options: { // 用babel-loader吧es6转换成es5
        presets: [ // 预设规则
          '@babel/preset-env'
        ],
        plugins: [ // 此处配置有顺序
          ["@babel/plugin-proposal-decorators", { "legacy": true }],
          ["@babel/plugin-proposal-class-properties", { "loose" : true }]
        ]
      }
    }
  ]
}

如果您手动包含插件并使用@babel/plugin-proposal-class属性,请确保@babel/plugin-proposal-class装饰器位于@babel/plugin-proposal-class属性之前。

npm i @babel/plugin-transform-runtime -D
npm i @babel/runtime -S

plugins: [ // 此处配置有顺序
                ["@babel/plugin-proposal-decorators", { "legacy": true }],
                ["@babel/plugin-proposal-class-properties", { "loose" : true }],
                "@babel/plugin-transform-runtime"
              ]

全局变量的引入(jQuery、Lodash...)

安装
npm i jquery -S

配置
let webpack = require('webpack')

new webpack.ProvidePlugin({ // 在每个模块中都注入$
  $: 'jquery'
})
当使用cdn在模板中引入时<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>可通过配置
externals: {
  jquery: "$"
},
此时可以通过$或window.$调用,再在文件中写import $ from 'jquery'时依然采用外部引入jQuery就不会被打包,打包文件不会增大。

打包图片

import lion from './static/img/img/lion' // 把图片引入返回一个新的图片地址

let image = new Image()
image.src = lion
document.body.appendChild(image)
background: url('./static/img/img/ABC.png')

file-loader(适用于前两种情况引入图片,貌似可以不用也没报错)

file-loader默认会生成一张图片到dist目录下,把生成图片的名字返回回来

安装
npm i file-loader -D
配置
{
  test: /\.(png|jpg|gif)$/,
  use: 'file-loader'
}

html-withimg-loader(适用于html标签引入图片)

安装
npm i html-withimg-loader -D
配置
{
  test: /\.html$/,
  use: 'html-withimg-loader'
}

url-loader(可取代file-loader包含file-loader的功能)

安装
npm i url-loader -D
配置
{
  test: /\.(png|jpg|gif)$/,
  use: {
    loader: 'url-loader',
    // 做一个限制,当图片小于多少k时用base64转化,否则用file-loader将图片产出
    options: {
      limit: 200*1024,
      // dist打包文件中的输出路径
      outputPath: '/img/'
    }
  }
}

打包多页面应用

代码在git上的morepage分支npx webpack运行打包
let path = require('path')
let HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  mode: 'development',
  // 多入口
  entry: {
    home: './src/index.js',
    other: './src/other.js'
  },
  output: {
    filename: '[name].[chunkhash].js',
    path: path.resolve(__dirname, 'dist')
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './index.html',
      filename: 'home.html',
      chunks: ['home'] // 模板引入homejs模块
    }),
    new HtmlWebpackPlugin({
      template: './index.html',
      filename: 'other.html',
      chunks: ['other'] // 模板引入otherjs模块
    })
  ]
}

配置source-map

  // 源码映射,打包时会在dist中单独生成一个source-map(.map)文件,出错时会标识打包前文件当前出错的类和行
  // devtool: 'source-map', // 增加的映射文件可以帮我们调试源代码,特点:大和全
  // devtool: 'eval-source-map', // 不会产生单独的映射文件会直接打包到打包文件中,可以产生报错行和列
  // devtool: 'cheap-module-source-map', // 不会产生列,会产生一个单独的映射文件
  devtool: 'cheap-module-eval-source-map', // 不会产生文件,也不会产生列
  performance: {
    hints:false
  },

watch的用法,修改代码后实时打包文件

watch: ture,
watchOptions: { // 监控选项
  poll: 1000, // 每秒问我一千次
  aggregateTimeout: 500, // 防抖,我一直输入代码
  ignored: /node_modules/ // 不需要监控的文件
}

webpack的三个插件

1.cleanWebpackPlugin:删除上次打包的文件

安装
npm i clean-webpack-plugin -D
配置
let CleanWebpackPlugin = require("clean-webpack-plugin")
new CleanWebpackPlugin()

2.copyWebpackPlugin:将文档等内容打包进打包文件

安装
npm i copy-webpack-plugin -D
配置
let CopyWebpackPlugin = require("copy-webpack-plugin")
new CopyWebpackPlugin([
  {from: './src/doc', to: './'}
])

3.bannerPlugin (内置):打包后文件中版权作者等标注信息

配置
let webpack = require('webpack')
new webpack.BannerPlugin('make 2019 by zhuyanlin')

webpack跨域

配置代理

proxy:{
"/fe": {
    target: "http://127.0.0.1:3000",
    changeOrigin: true,
    ws: true,
    pathRewrite: {
      "^/fe": ""
    }
  }
}

resolve

解析顺序别名等配置
https://www.webpackjs.com/configuration/resolve/#resolve

定义环境变量

new webpack.DefinePlugin({
  DEV: JSON,stringify('dev'),
  AAA: 'true'
})

区分不同环境

将不环境配置文件分开

npm i webpack-merge -D

noParse

不去解析某些包中的依赖关系

IgnorePlugin moment库(时间插件)

npm i moment -S
import moment from 'moment';
console.log(moment().endOf('day').fromNow())

dullPlugin

npm i react react-dom -S
npm i @babel/preset-react -D
{
        test: /\.js$/,
        use: [
          {
            loader: 'babel-loader',
            options: { // 用babel-loader吧es6转换成es5
              presets: [ // 预设规则
                '@babel/preset-env',
                '@babel/preset-react'
              ],
              plugins: [ // 此处配置有顺序
                ["@babel/plugin-proposal-decorators", { "legacy": true }],
                ["@babel/plugin-proposal-class-properties", { "loose" : true }]
                // "@babel/plugin-transform-runtime"
              ]
            }
          }
        ],
        // exculde: /node_modules/
        // inculde: path.resolve(__dirname, 'src')
      },

。。。未完待续

webpack生命周期

http://taobaofed.org/blog/2016/09/09/webpack-flow/

image.png

webpack搭建一个react项目并优化

npm init // 创建package.json
npm i webpack -D
npm i react react-dom -S
npm i @babel/core babel-loader @babel/preset-env @babel/preset-react --save-dev
npm i html-webpack-plugin html-loader --save-dev
npm i webpack-dev-server --save-dev

// webpack.config.json
const path = require('path');
const HtmlWebPackPlugin = require("html-webpack-plugin");
module.exports = {
    entry: path.resolve(__dirname, './src/index.js'), //指定入口文件,程序从这里开始编译,__dirname当前所在目录, ../表示上一级目录, ./同级目录
    output: {
        path: path.resolve(__dirname, './dist'), // 输出的路径
        filename: 'bundle.js'  // 打包后文件
    },
    module: {
        rules: [
            {
                test: /\.(js|jsx)$/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: ["@babel/preset-env", "@babel/preset-react"],
                    }
                },
                exclude: /node_modules/
            }
        ]
    },
    plugins: [
      new HtmlWebPackPlugin({
        template: "./index.html",
        filename: "./index.html"
      })
    ],
    devServer: {
      contentBase: path.join(__dirname, "dist"),
      compress: true,
      port: 9000
    }
}

HappyPack

分进程变异

npm i -D happypack

懒加载和预加载,webpack生产环境自带优化,摇树优化和变量提升
抽离公共代码用于打包多入口项目

上一篇下一篇

猜你喜欢

热点阅读