webpack study notes

command usage

npm install webpack -g
    作为全局安装, 在任意目录使用

npm install webpack --save-dev
    作为项目依赖安装

npm init
    创建package.json

npm install webpack-dev-server --save-dev
    使用webpack-dev-server启动服务器

webpack --progress -colors
    让编译的输出内容带有进度和颜色

webpack --watch
    如果不想每次修改模块后都重新编译, 那么可以启动监听模式。
    开启监听模式后, 没有变化的模块会在编译后缓存到内存中,
    而不会每次都被重新编译, 所以监听模式的整体速度是很快的

webpack --display-error-details
    用来打印错误详情

npm install xxx-loader --save-dev
    安装多个加载器: npm install babel-core babel-preset-es2015 babel-preset-react

npm webpack --config webpack.config.js
    执行打包命令

npm start
    启动开发模式下的server

npm run start:prod
    启动生产模式的server

npm run build
    打包生产模式的代码

npm run lint: eslint
    代码检查

npm run lint:watch: eslint
    监视

npm run remove:build
    删除dist目录

npm run clean:build
    清除dist目录

// 调用webpack
webpack
    开发环境下编译

webpack -p
    产品编译及压缩

webpack --watch
    开发环境下持续的监听文件变动来进行编译

webpack -d
    引入source maps

configuration file

webpack.config.dev.js: 开发模式相关配置
webpack.config.prod.js: 生产模式相关配置
server.js: 配置本地的server(包含dev server和prod server) 将server部分分离到一个单独到的文件配置
package.json
//webpack.config.dev.js

var webpack = require('webpack');
var path = require('path');
var config = {
    // 入口文件配置
    entry: {
        path.resolve(__dirname, 'app/index.js');
    },
    // 文件输出配置
    output: {
        path: path.resolve(_dirname, 'build'),
        filename: 'bundle.js',
        publicPath: '/'
    },

    // 插件项
    plugins: [],

    // 加载器配置
    module: {
        loaders: [
            {
                test: /pattern/,
                loader: 'xxx-loader',
                exclude: /dir/,
                query: {
                    presets: ['react']
                }
            },
             {
                 test: /\.(png|jpg)$/,
                 loader: 'url-loader?limit=8192'
                 // 内联的base64的图片地址, 图片要小于8k, 直接的url的地址则不解析
             }
        ]
    },
    // 其他解决方案配置
    resolve: {
        extensions: ['', '.js', '.json'],
        alias: {}
    },
    watch: true
};

module.exports = config;
webpack.server.js

var webpack = require('webpack');
var webpackServer = require('webpack-dev-server');
var config = require('./webpack.config.dev.js');

var compiler = webpack(config);
var server = new webpackDevServer(compiler, {
    contentBase: './app',
    historyApiFallback: true,
    hot: true,        //热启动
    inline: true, // 监控js变化
    stats: {
        color: true
    }
});

config.entry.unshift('webpack-dev-server/client?http://localhost:8080/',
    'webpack/hot/dev-server');

server.listen(8080, 'localhost', function(err) {
    if(err) {
        console.log(err);
    }
    console.log('Listening at localhost:8080...');
});

<!-- package.json -->
'script': {
    'start': 'node server.js'
}

Detailed configuration

entry: 入口, 定义要打包的文件

output: 出口, 定义打包输出的文件;包括路径, 文件名,还可能有运行时的访问路径(publicPath)参数

module: webpack将所有的资源都看做是模块, 而模块就需要加载器;
|---- loaders: 主要定义一些loaders, 定义哪些后缀名的文件应该用哪些loader

|-------- test: 匹配文件后缀, 检测哪些文件需要此loader, 是一个正则表达式

|-------- exclude: 忽略哪些文件

|-------- query: 参数 (或直接写于loader如: loader: 'url-loader?limit=8192')

|------------ presets:

resolve: 其他解决方案配置

|---- extensions: 忽略文件扩展名, require文件时可直接使用require('file'),而非带后缀如require('file.js')

|-------- alias: 模块别名定义,方便后续直接饮用别名无需多写长地址, 后续直接require(key)

plugins: 定义一些额外的插件

watch: 值为boolean, 监听文件变化

Configure production environment

开发环境:
    webpack.config.dev.js
    需要日志输出, sourcemap, 错误报告等

生产环境:
    webpack.config.prod.js
    需要做代码压缩, 对文件名进行hash处理等

Differentiate the environment

Use DefinePlugin to set environment variables, and decide whether to package and compress and start dev server or prod server according to the set environment variables

plugins: [
    new webpack.DefinePlugin({
        'process.evn.NODE_ENV': JSON.stringify('production')
    });
]

Determine if the current environment is production

var isProduction = function() {
    return process.env.NODE_ENV === 'production';
}


output: {
    path: path.resolve(isProduction ? '__build' : './assets/'),
    filename: isProduction ? '[name].js' : './assets/js/[chunkhash:8].[name].min.js',
    chunkFilename: isProduction ? '[chunkhash:8].chunk.js' : './assets/js/[chunkhash:8].chunk.min.js',
    publicPath: isProduction ? '/__build/' : 'http://cdn.site.com/'
}

code compression

new webpack.optimizeUglifyJsPlugin({
    compress: {
        warnings: false
    }
});

Add Hash cache

For unmodified files, get files from the cache, for modified files, don't get them from the cache

output: {
    //chunkhash 默认16位, 可自定义配置
    filename: '[chunkhash:8].bundle.js'
}

Automatically generate pages

After the file name has a hash value, this value will change every time it is compiled, and it is necessary to manually modify the referenced file name in the html file. This repetitive work is trivial and error-prone. Here we can use html-webpack -plugin to help us handle this automatically, to simplify creating HTML files for webpackbundle

Solution: Build an index.tpl.html in the project directory as a hook

    <!-- index.tpl.html -->
    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="UTF-8">
        <title>My APP</title>
    </head>
    <body>
        <div id="app"></div>
    </body>
    </html>

Add configuration code to webpack.config.dev.js and webpack.config.prod.js to generate corresponding index.html

    plugins: [
        new HtmlWebpackPlugin({
            template: 'app/index.tpl.html',
            inject: 'body',
            filename: index.html
        })
    ]

Loader

js processing

babel-loader: 转换JSX
babel-core: babel核心包
babel-preset-react
babel-preset-es2015
<!-- webpack.config.dev.js -->
<!-- babel-loader配置 -->
loaders:[
    {
        loaders: 'xxx-loader',
        query: {
            resets:['react', 'es2015']
        }
    }
]

css processing

style-loader
css-loader
less-loader

img processing

url-loader
    可以根据自定义文件大小或者转化为 base64 格式的 dataUrl, 或者单独作为文件, 也可以自定义对应的hash 文件名
file-loader
    默认情况下会根据图片生成对应的 MD5hash 的文件格式
image-webpack-loader
    提供压缩图片的功能

Loading babel-loader requires configuring the query parameter

<!-- webpack.config.dev.js -->
<!-- url-loader配置 -->
loaders:[
    {
        test: /\.(jpe?g|png|gif|svg)$/i,
        loaders: [
            // 当内容size小于8KB时, 会自动转成base64的方式内嵌进去, 这样可以减少一个HTTP的请求
            // 当图片大于8KB时, 则会在img/下生成压缩后的图片, 命名是[hash:8].[name].[ext]的形式
            // hash:8的意思是取图片内容hashsum值的前8位,
            // 这样做能够保证引用的是图片资源的最新修改版本, 保证浏览器端能够即时更新
            'url?limit=8192&name=img/[hash:8].[name].[ext]',

            // 图片压缩
            'image-webpack'
        ]
    }
]
<!-- webpack.config.dev.js -->
<!-- file-loader配置 -->
loaders:[
    {
        test: /\.(jpe?g|png|gif|svg)$/i,
        loaders: [
            // 生成md5 hash格式的文件名
            'file?hash=sha512&digest=hex&name=[hash].[ext]',
            // 图片压缩
            'image-webpack'
        ]
    }
]

plugin

<!-- webpack.config.dev.js -->
plugins: [definPlugin, bannerPlugin, uglifyJsPlugin...]

Set environment variables

var definPlugin = new webpack.DefinePlugin({
    "process.env": {
        NODE_ENV: JSON.stringify("production")
    }
    // feature flags: 在开发环境(例如日志)活内部测试环境(例如没有发布的新功能)中使用
    // __DEV__: JSON.stringify(JSON.parse(process.env.BUILD_DEV || 'true')),
    // __PRERELEASE__: JSON.stringify(JSON.parse(process.env.BUILD_PRERELEASE || 'false'))
});

Add comments to the header of the output file

var bannerPlugin = new webpack.BannerPlugin('This is test!');

JS Obfuscation

var uglifyJsPlugin = new webpack.optimize.UglifyJsPlugin({
    mangle: {
        // 配置以下列表, 在混淆代码时, 以下配置的变量, 不会被混淆
        except: ['$super', '$', 'exports', 'require']
    }
});

Compress JS

var minChunkSizePlugin = new webpack.optimize.MinChunkSizePlugin({
    compress: {
        warnings: false
    }
});

Minify React

var definPlugin = new webpack.DefinePlugin({
    'process.env': {
        NODE_ENV: JSON.stringify('production')
    }
});

load jQuery

new webpack.ProvidePlugin({
    $: 'jquery'
});

Common module extraction

new webpack.optimize.CommonsChunkPlugin({
    name: 'vendors',                     // 将公共模块提取, 生成名为`vendors`的chunk
    chunks: ['index','list','about'],     //提取哪些模块共有的部分
    minChunks: 3                         // 提取至少3个模块共有的部分
});

Load css with link tag alone and set path

new ExtractTextPlugin('css/[name].css'), // 相对于output配置中的publickPath

Automatically generate html files, related configurations of template generation, and each configuration for a page, write several

new HtmlWebpackPlugin({                 //根据模板插入css/js等生成最终HTML
    favicon: './src/img/favicon.ico',     //favicon路径, 通过webpack引入同时可以生成hash值
    filename: './view/index.html',         //生成的html存放路径, 相对于path
    template: './src/view/index.html',     //html模板路径
    inject: 'body',                     //js插入的位置, true/'head'/'body'/false
    hash: true,                         //为静态资源生成hash值
    chunks: ['vendors', 'index'],        //需要引入的chunk, 不配置就会引入所有页面的资源
    minify: {                             //压缩HTML文件
        removeComments: true,             //移除HTML中的注释
        collapseWhitespace: false         //删除空白符与换行符
    }
});
new HtmlWebpackPlugin({                 //根据模板插入css/js等生成最终HTML
    favicon: './src/img/favicon.ico',     //favicon路径, 通过webpack引入同时可以生成hash值
    filename: './view/list.html',         //生成的html存放路径, 相对于path
    template: './src/view/list.html',     //html模板路径
    inject: true,                         //js插入的位置, true/'head'/'body'/false
    hash: true,                         //为静态资源生成hash值
    chunks: ['vendors', 'list'],        //需要引入的chunk, 不配置就会引入所有页面的资源
    minify: {                             //压缩HTML文件
        removeComments: true,             //移除HTML中的注释
        collapseWhitespace: false         //删除空白符与换行符
    }
});

Other plugins

new webpack.HotModuleReplacementPlugin()     // 热加载
HotModuleReplacementPlugin()                 // 代码热替换
NoErrorsPlugin()                             // 报错但不退出webpack进程
OpenBrowserPlugin()                         // 自动打开浏览器

webpack use

Analyze common code extraction of multiple modules and package them individually

var commonsPlugin = new webpack.optimize.CommonsChunkPlugin('common.js');
module.exports = {
    entry: {
        page1: './main1.js',
        page2: './main2.js'
    },
    output: {
        path: 'build',
        filename: '[name].js'
    },
    plugins: [
        commonsPlugin
    ]
}

file reference ignore extension configuration

If you want to omit the file extension when requiring a file, just add it to webpack.config.js  resolve.extensions to configure it.

// webpack.config.js
module.exports = {
    entry: './main.js',
    output: {
        filename: 'bundle.js'
    },
    module: {
        loaders: [
            { test: /\.coffee$/, loader: 'coffee-loader' },
            {
                test: /\.js$/,
                loader: 'babel-loader',
                query: {
                    presets: ['es2015', 'react']
                }
            }
        ]
    },
    resolve: {
        // 现在你require文件的时候可以直接使用require('file'), 不用使用require('file.coffee')
        extensions: ['', '.js', '.json', '.coffee']
    }
};

Loading css styles and images

First you need require()to  load your static resources (named as they would with node's require()):

require('./bootstrap.css');
require('./myapp.less');

var img = document.createElement('img');
img.src = require('./glyph.png');

When you require a CSS (less or other) file, webpack will insert an inline in the page <style>to import the styles. When requiring an image, the bundle file will contain the url of the image and require()return the url of the image.

But this requires you webpack.config.jsto configure it accordingly (loaders are still used here)

// webpack.config.js
module.exports = {
    entry: './main.js',
    output: {
        path: './build', // 图片和js会放在这
        publicPath: 'http://mycdn.com/', // 这里用来生成图片的地址
        filename: 'bundle.js'
    },
    module: {
        loaders: [
            {
                test: /\.less$/,
                loader: 'style-loader!css-loader!less-loader'
            }, // 用!去链式调用loader
            {
                test: /\.css$/,
                loader: 'style-loader!css-loader'
            },
            {
                test: /\.(png|jpg)$/,
                loader: 'url-loader?limit=8192'
                // 内联的base64的图片地址, 图片要小于8k, 直接的url的地址则不解析
            }
        ]
    }
};

Feature flags

Some code in the project is only used in the development environment (such as logs) or in the internal test environment (such as those new functions that have not been released), so we need to introduce the following magic globals:

if (__DEV__) {
    console.warn('Extra logging');
}
// ...
if (__PRERELEASE__) {
    showSecretFeature();
}

Also configure these variables in webpack.config.js so that webpack can recognize them.

// webpack.config.js

// definePlugin 会把定义的string 变量插入到Js代码中。
var definePlugin = new webpack.DefinePlugin({
    __DEV__: JSON.stringify(JSON.parse(process.env.BUILD_DEV || 'true')),
    __PRERELEASE__: JSON.stringify(JSON.parse(process.env.BUILD_PRERELEASE || 'false'))
});

module.exports = {
    entry: './main.js',
    output: {
        filename: 'bundle.js'
    },
    plugins: [definePlugin]
};

After the configuration is complete, you can use  BUILD_DEV=1 BUILD_PRERELEASE=1 webpackto package the code.
It is worth noting that  webpack -p all non-functional code will be deleted, that is, those code blocks wrapped under these global variables will be deleted, so as to ensure that these codes will not be leaked due to release.

Multiple entry files

If you have two pages: profile and feed. If you want users to visit the profile page without loading the code of the feed page, you need to generate multiple bundles files: create its own "main module" (entry file) for each page.

// webpack.config.js
module.exports = {
    entry: {
        Profile: './profile.js',
        Feed: './feed.js'
    },
    output: {
        path: 'build',
        filename: '[name].js' // name是基于上边entry中定义的key
    }
};

Insert in profile page <script src="build/Profile.js"></script>. The same goes for feeds.

Optimize common code

There is a lot of common code (such as React, common styles and components, etc.) for the Feed and Profile pages. Webpack can extract the common code between pages and generate a common bundle file for the two page caches:

// webpack.config.js

var webpack = require('webpack');

var commonsPlugin =
    new webpack.optimize.CommonsChunkPlugin('common.js'); // 引入插件

module.exports = {
    entry: {
        Profile: './profile.js',
        Feed: './feed.js'
    },
    output: {
        path: 'build',
        filename: '[name].js' // 为上面entry的key值
    },
    plugins: [commonsPlugin]
};

Import before importing your own bundle in the previous step<script src="build/common.js"></script>

Asynchronous loading

While CommonJS is loaded synchronously, webpack also provides a way to load asynchronously. This is useful for client-side routing used in single-page applications. When it is actually routed to a page, its code will be loaded.

Specify the split point you want to load asynchronously  . see the example below

if (window.location.pathname === '/feed') {
    showLoadingState();
    require.ensure([], function() { // 这个语法痕奇怪, 但是还是可以起作用的
        hideLoadingState();
        require('./feed').show(); // 当这个函数被调用的时候, 此模块是一定已经被同步加载下来了
    });
} else if (window.location.pathname === '/profile') {
    showLoadingState();
    require.ensure([], function() {
        hideLoadingState();
        require('./profile').show();
    });
}

The rest can be left to webpack, which will generate and load these additional  chunk  files for you.

By default, webpack will import these chunk files from the root directory of the project. You can also  output.publicPathconfigure the import path of the chunk file by

// webpack.config.js
output: {
    path: "/home/proj/public/assets", // webpack的build路径
    publicPath: "/assets/" // 你require的路径
}

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326611208&siteId=291194637