怪咖晨

webpack4 开发环境搭建

。一笑奈何 webpack4webpack-chainwebpack-cli/initjavascript

webpack 环境搭建

npm 初始化项目

yarn init

安装webpack依赖

yarn add webpack webpack-dev-server webpack-cli @webpack-cli/init --dev

webpack 初始化

npx webpack-cli init

? Will your application have multiple bundles? Yes
? Type the names you want for your modules (entry files), separated by comma [example: app,vendor] app
? What is the location of "app"? [example: ./src/app] ./src/app
? Which folder will your generated bundles be in? [default: dist]: dist
? Will you be using ES2015? Yes
? Will you use one of the below CSS solutions? SASS
yarn add v1.12.1

添加编译插件

## 二选一
## 1. class 写法 向下兼容
yarn add @babel/plugin-proposal-class-properties --dev
## 2. babel 支持 import 
## yarn add @babel/plugin-syntax-dynamic-import --dev
yarn add @babel/preset-env @babel/core cross-env --dev

安装loader

yarn add html-withimg-loader url-loader file-loader style-loader postcss-loader --dev

插件安装

yarn add mini-css-extract-plugin uglifyjs-webpack-plugin clean-webpack-plugin copy-webpack-plugin extract-css html-webpack-plugin --dev

安装webpack-chain

yarn add webpack-chain --dev

此处webpack-chain5.0.1版本,是针对webpack 4维护的,需特别注意

github地址:webpack-chain

创建webpack.config.js

const path = require('path');
const isProd = process.env.NODE_ENV === 'production';
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

const WebpackChain = require('webpack-chain');

const config = new WebpackChain();

config.when(isProd,config=>{
    config.entry('index').add('./src/index.js');
}).when(!isProd,config=>{
    config.entry('index').add('./src/index.js');
})
// Modify output settings
    .output
    .path(path.join(__dirname, "dist")).filename('[name].js').end()
    .when(isProd, config => {
        config.mode('production');
    }).when(!isProd,config=>{
    config.mode('development').devtool('source-map');
}).end();

/**
 * module
 */
config
    .module
    .rule("compile")
    .test(/\.js$/)
    .include.add(path.join(__dirname,'src')).end()
    .exclude.add(/node_modules/).end()
    .use('babel').loader("babel-loader")
    .options({
        presets: ['@babel/preset-env'],
        plugins: ['@babel/plugin-proposal-class-properties']
    });

config.module
    .rule('images')
    .test(/\.(png|jpg|jpeg|gif)/)
    .use('url-loader')
    .loader('url-loader')
    .options({
        limit: 1 * 1024,
        name: path.posix.join("images","[name].[ext]")
    })

// do not base64-inline SVGs.
// https://github.com/facebookincubator/create-react-app/pull/1180
config.module
    .rule('svg')
    .test(/\.(svg)(\?.*)?$/)
    .use('url-loader')
    .loader('url-loader')
    .options({
        limit: 1024 * 3,//30kb
        fallback: 'file-loader'
    })

config.module
    .rule("fonts")
    .test(/\.(woff2?|eot|ttf|otf)(\?.*)?$/i)
    .use('url-loader')
    .loader('url-loader')
    .options({
        limit: 10000,
        fallback: {
            loader: 'file-loader',
            options: {
                name: path.posix.join("fonts","[name].[ext]")
            }
        }
    });

config.when(isProd,config=>{
    config.module.rule("css").test(/\.(sa|sc|c)ss$/)
        .use("style").loader(MiniCssExtractPlugin.loader);
}).when(!isProd,config=>{
    config.module.rule("css").test(/\.(sa|sc|c)ss$/)
        .use("style-loader").loader("style-loader");
});

config.module.rule("css").test(/\.(sa|sc|c)ss$/)
    .use('css').loader("css-loader").end()
    .use('postcss-loader').loader('postcss-loader');

config.module.rule("scss").test(/\.(sa|sc)ss$/).use("sass-loader").loader("sass-loader");

config.module.rule("lass").test(/\.less$/).use("less-loader").loader("less-loader");

//config.module.rule("html").test(/\.(htm|html)$/i).use("html").loader('html-withimg-loader');

/**
 * plugin
 */
config.when(isProd,config=>{
    const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
    const CleanWebpackPlugin = require('clean-webpack-plugin');
    const CopyWebpackPlugin = require("copy-webpack-plugin");

    config.plugin("clear").use(new CleanWebpackPlugin([path.join(__dirname, 'dist')]));
    config.optimization.splitChunks({
        cacheGroups: {
            commons: {
                chunks: "initial",
                name: "common",
                minChunks: 2,
                maxInitialRequests: 5, // The default limit is too small to showcase the effect
                minSize: 0, // This is example is too small to create commons chunks
                reuseExistingChunk: true // 可设置是否重用该chunk(查看源码没有发现默认值)
            }
        }
    });
    config.plugin("js").use(new UglifyJSPlugin({}));
    config.plugin('extract-css')
        .use(MiniCssExtractPlugin, [{
            filename: "css/[name].css",
            chunkFilename: "css/[name].css"
        }]);
    // config.plugin('copy').use(new CopyWebpackPlugin([
    //     {
    //         from:"./src/sass",
    //     }
    // ]))
})

const HtmlWebpackPlugin = require('html-webpack-plugin');
config.plugin("html").use(HtmlWebpackPlugin, [{
    /*
     template 参数指定入口 html 文件路径,插件会把这个文件交给 webpack 去编译,
     webpack 按照正常流程,找到 loaders 中 test 条件匹配的 loader 来编译,那么这里 html-loader 就是匹配的 loader
     html-loader 编译后产生的字符串,会由 html-webpack-plugin 储存为 html 文件到输出目录,默认文件名为 index.html
     可以通过 filename 参数指定输出的文件名
     html-webpack-plugin 也可以不指定 template 参数,它会使用默认的 html 模板。
     */
    template: "./public/index.html",
    filename:"index.html",
    /*
     因为和 webpack 4 的兼容性问题,chunksSortMode 参数需要设置为 none
     https://github.com/jantimon/html-webpack-plugin/issues/870
     */
    chunksSortMode: 'none',
    xhtml: true,
    minify: {
        collapseWhitespace: false, //删除空格,但是不会删除SCRIPT、style和textarea中的空格
        conservativeCollapse: false, //删除空格,总是保留一个空格
        removeAttributeQuotes: false, //删除引号,删除不需要引号的值
        useShortDoctype: false, //使用短的文档类型
        removeComments: true,
        collapseBooleanAttributes: true,
        removeScriptTypeAttributes: true
        // more options:
        // https://github.com/kangax/html-minifier#options-quick-reference
    }
}]);


config.when(isProd,config=>{

}).when(!isProd,config=>{
    config.devServer.host('localhost').port(8080).open(process.os === 'darwin');
})

config.resolve.alias.set("@",path.join(__dirname,"src"));

// Export the completed configuration object to be consumed by webpack
module.exports = config.toConfig();

添加启动脚本

修改package.json文件中scripts节点,加入如下配置:

  "scripts": {
    "dev": "cross-env NODE_ENV=development webpack-dev-server",
    "build": "cross-env NODE_ENV=production webpack"
  }

创建postcss.config.js

module.exports = {
    plugins: {
        autoprefixer: {
            "browsers": [
                "ie >= 9",
                "ff >= 30",
                "chrome >= 34",
                "safari >= 7",
                "opera >= 23"
            ]
        }
    }
}

创建src/index.js

export default  class demo {
    constructor(){
        console.log("init");
    }
}

创建example/index.js

import Demo from "../src/index";

const demo = new Demo();

根目录创建index.html

启动

yarn run dev
。一笑奈何
高山仰止,景行行止。虽不能至,心向往之