用webpack构建您的前端项目

Posted 前端school

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了用webpack构建您的前端项目相关的知识,希望对你有一定的参考价值。

组织分享一篇webpack知识点,这里主要说方法。用于大家快速上手!
webpack概念

webpack是一个模块化打包工具,使用js作为载体将所有静态资源打包在一起,支持的loader和plugin等能对各种静态资源进行预处理,极大地方便了前端的工程化开发。详情参考官网,此处放张官网概念图:

构建流程

环境要求

nodejs
tips: 装好nodejs后可以使用淘宝NPM镜像替代官方版本
npm install -g cnpm --registry=https://registry.npm.taobao.org

项目初始化

创建项目

创建一个webpack-demo项目并生成package.json文件。之后所有操作的根目录均为webpack-demo/


1

2

3

mkdir webpack-demo

cd webpack-demo

npm init -f



引入webpack2


1

cnpm i -D webpack@beta


项目初始结构分析

  • 通常项目会分成三个运行环境:开发人员在本地跑的开发环境(dev)、测试人员用来做黑盒测试的测试环境(test)和线上运行的生产环境(production)。
    简单起见,本文只考虑开发环境(dev)和生产环境(prod),测试环境可以自行类比。

  • 综上,webpack的配置需要有两套,同时两套配置必然会存在相同的部分,故新建目录与文件如下图:

    同时引入一个库webpack-merge用于合并base config和特定环境的config

    1

    cnpm i -D webpack-merge

开始写webpack config

webpack-config/base.js


1

2

3

module.exports = {

//common config

};



webpack-config/dev.js, webpack-config/prod.js


1

2

3

4

5

6

const webpackMerge = require('webpack-merge');

const base = require('./base');

module.exports = webpackMerge(base, {

//specific config

});



webpack.config.js


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

const devModule = require('./webpack-config/dev');

const prodModule = require('./webpack-config/prod');

let finalModule = {};

let ENV = process.env.NODE_ENV;     //此处变量可由命令行传入

switch (ENV) {

case 'dev':

finalModule = devModule;

break;

case 'prod':

finalModule = prodModule;

break;

default:

break;

}

module.exports = finalModule;



编写npm scripts,区分环境

package.json


1

2

3

4

5

6

7

8

9

10

11

12

{

"name": "webpack-demo",

"version": "1.0.0",

"scripts": {

"dev": "cross-env NODE_ENV=dev webpack",

"prod": "cross-env NODE_env=prod webpack"

},

"devDependencies": {

"webpack": "^2.2.0",

"webpack-merge": "^2.6.1"

}

}



由于*unix和windows设置NODE_ENV的语句有所差异,此处用到了一个库cross-env以达到兼容的目的


1

cnpm i -D cross-env



从dev环境写起

新建一个src目录用户存放项目源文件,同时在src下新建一个index.js作为打包的入口

webpack-dev-server

  • 安装webpack-dev-server

    1

    cnpm i -D webpack-dev-server

  • 配置webpack config
    webpack-config/dev.js

    1

    2

    3

    4

    5

    6

    7

    8

    ...

    module.exports = webpackMerge(base, {

    entry: process.cwd() + '/src/index.js',

    output: {

    filename: '[name].bundle.js'

    },

    devtool: 'eval-source-map'   //enable srouce map

    });

  • 修改npm scripts
    package.json

    1

    2

    3

    4

    5

    6

    7

    {

    ...

    "scripts": {

    "dev": "cross-env NODE_ENV=dev webpack-dev-server --inline --hot --host 0.0.0.0",

    ...

    }

    }

htmlWebpackPlugins

src目录下新建一个index.html作为template,htmlWebpackPlugins会根据这个template生成网站的index.html,同时自动写入bundle依赖。
src下放一个favicon.ico作为网站的icon

  • 安装htmlWebpackPlugins

    1

    cnpm i -D html-webpack-plugin

  • 配置webpack config
    webpack-config/dev.js

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    ...

    const HtmlWebpackPlugin = require('html-webpack-plugin');

    ...

    module.exports = webpackMerge(base, {

    ...

    plugins: [

    new HtmlWebpackPlugin({

    filename: 'index.html',

    template: process.cwd() + '/src/index.html',

    favicon: process.cwd() + '/src/index.html'

    })

    ]

    ...

    });

到这一步算是完成了最基本的开发环境配置,命令行执行npm run dev,然后浏览器打开localhost:8080就能看到成果
将npm scripts中的start命令指向npm run dev,这样每次开始开发只需要执行npm start
package.json


1

2

3

4

5

6

7

8

9

{

...

"scripts": {

...

"start": "npm run dev"

...

}

...

}



Loaders(Rules)

webpack本身只能处理js模块,如果需要处理其他类型的文件,就需要Loaders进行转换

ES6+支持

ES6+虽然不能直接被浏览器全部识别,但是能用babel转换成ES5代码。

  • 安装babel编译相关依赖

    1

    cnpm i -D babel-core babel-preset-latest babel-preset-stage-2 babel-runtime babel-plugin-transform-runtime babel-loader

  • 新建.babelrc文件并写入:
    .babelrc

    1

    2

    3

    4

    {

    "presets": ["latest", "stage-2"],

    "plugins": ["transform-runtime"]

    }

  • 配置rules
    webpack-config/dev.js

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    module.exports = webpackMerge(base, {

    ...

    module: {

    rules: [

    ...

    {

    test: /\.js$/,

    exclude: [/node_modules/],

    loader: 'babel-loader'

    }

    ...

    ]

    }

    ...

    });

css处理

现如今css预处理器已经成为前端开发的标配,Sass(Scss),Less和Stylus各行其道,个人偏好scss。
PostCss可以作为一个后处理器,实现为css自动添加浏览器前缀等功能

  • 安装相关依赖

    1

    cnpm i -D style-loader css-loader postcss-loader autoprefixer node-sass sass-loader

  • 新建postcss.config.js
    postcss.config.js

    1

    2

    3

    4

    5

    module.exports = {

    plugins: [

    require('autoprefixer')({browsers: ['last 2 versions', 'ios 7', 'Firefox > 20']})

    ]

    };

  • 配置rules
    webpack-config/dev.js

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    module.exports = webpackMerge(base, {

    ...

    module: {

    rules: [

    ...

    {

    test: /\.scss$/,

    exclude: [/node_modules/],

    use: [

    'style-loader',

    {

    loader: 'css-loader',

    options: {

    sourceMap: true

    }

    },

    'postcss-loader',

    {

    loader: 'sass-loader',

    options: {

    sourceMap: true

    }

    }

    ]

    }

    ...

    ]

    }

    ...

    });

html模板、图片等的处理
  • 安装相关依赖

    1

    cnpm i -D html-loader file-loader url-loader

  • 配置rules
    webpack-config/dev.js

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    module.exports = webpackMerge(base, {

    ...

    module: {

    rules: [

    ...

    {

    test: /\.html$/,

    loader: 'html-loader',

    options: {

    minimize: true

    }

    },

    {

    test: /\.(jpe?g|png|gif|svg)$/i,

    loader: 'url-loader',

    options: {

    limit: 10000,

    hash: 'sha512',

    publicPath: '/',

    name: 'assets/images/[hash].[ext]'

    }

    }

    ...

    ]

    }

    ...

    });

至此,dev环境配置完成

配置production环境

抽出公共部分

  • entry可以共用,prod的output需要加上文件chunkhash用来刷新缓存,并将文件输出至dist目录
    webpack-config/dev.js

    1

    - entry: process.cwd() + '/src/index.js',

    webpack-config/base.js

    1

    2

    3

    module.exports = {

    entry: process.cwd() + '/src/index.js',

    };

    webpack-config/prod.js

    1

    2

    3

    4

    5

    6

    7

    8

    9

    const webpackMerge = require('webpack-merge');

    const base = require('./base');

    module.exports = webpackMerge(base, {

    output: {

    filename: 'bundle.[chunkhash].js',

    path: process.cwd() + '/dist'

    },

    });

    HtmlWebpackPlugin公共
    webpack-config/dev.js

    1

    2

    3

    4

    5

    -new HtmlWebpackPlugin({

    -  filename: 'index.html',

    -  template: process.cwd() + '/src/index.html',

    -  favicon: process.cwd() + '/src/index.html'

    -})

    webpack-config/base.js

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    const HtmlWebpackPlugin = require('html-webpack-plugin');

    module.exports = {

    ...

    plugins: [

    new HtmlWebpackPlugin({

    filename: 'index.html',

    template: process.cwd() + '/src/index.html',

    favicon: process.cwd() + '/src/index.html'

    })

    ],

    };

    js、html和图片loader公共,prod的css loader需要使用ExtractTextPlugin将css从js中分离出来
    webpack-config/dev.js

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    -{

    -  test: /\.js$/,

    -  exclude: [/node_modules/],

    -  loader: 'babel-loader'

    -}

    ...

    -{

    -  test: /\.html$/,

    -  loader: 'html-loader',

    -  options: {

    -    minimize: true

    -  }

    -},

    -{

    -  test: /\.(jpe?g|png|gif|svg)$/i,

    -  loader: 'url-loader',

    -  options: {

    -    limit: 10000,

    -    hash: 'sha512',

    -    publicPath: '/',

    -    name: 'assets/images/[hash].[ext]'

    -  }

    -}

    webpack-config/base.js

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    ...

    module.exports = {

    ...

    module: {

    rules: [

    {

    test: /\.js$/,

    exclude: [/node_modules/],

    loader: 'babel-loader'

    },

    {

    test: /\.html$/,

    loader: 'html-loader',

    options: {

    minimize: true

    }

    },

    {

    test: /\.(jpe?g|png|gif|svg)$/i,

    loader: 'url-loader',

    options: {

    limit: 10000,

    hash: 'sha512',

    publicPath: '/',

    name: 'assets/images/[hash].[ext]'

    }

    }

    ]

    }

    };

    1

    cnpm i -D extract-text-webpack-plugin@beta

    webpack-config/prod.js

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    ...

    const ExtractTextPlugin = require("extract-text-webpack-plugin");

    ...

    module: {

    rules: [

    {

    test: /\.scss$/,

    exclude: [/node_modules/],

    use: ExtractTextPlugin.extract({

    fallback: 'style-loader',

    use: [

    {

    loader: 'css-loader',

    options: {

    minimize: true

    }

    },

    'postcss-loader',

    'sass-loader'

    ]

    })

    }

    ]

    },

    ...

    plugins: [

    new ExtractTextPlugin({

    filename: "bundle.[chunkhash].css"

    })

    ],

production环境的其它处理

使用UglifyJsPlugin压缩js


1

cnpm i - D uglifyjs-webpack-plugin


webpack-config/prod.js


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

...

const UglifyJSPlugin = require('uglifyjs-webpack-plugin');

...

module.exports = webpackMerge(base, {

...

const webpack = require('webpack');

...

plugins: [

...

new UglifyJSPlugin({

compress: {

warnings: false,

},

output: {

comments: false

}

})

...

]

...

})

...



使用CleanWebpackPlugin清空output目录

构建前先清空,防止出现垃圾文件


1

cnpm i -D clean-webpack-plugin



webpack-config/prod.js


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

...

module.exports = webpackMerge(base, {

...

plugins: [

...

const CleanWebpackPlugin = require('clean-webpack-plugin');

...

new CleanWebpackPlugin(['dist'], {

root: process.cwd(),

exclude: []

})

...

]

...

})

...



至此,production环境配置完毕,同时抽出了公共部分

配置resolve.alias

  • 开发的时候如果有一个很深的目录比如:src/a/b/c/d/, 然后在d目录下的一个模块需要引入a目录下的模块,需要这样写:import '../../../some-module',为了方便可以配置一个为src目录配置一个alias,这样模块引入只需要这样写:import src/a/some-module
    webpack-config/base.js

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    ...

    const path = require('path');

    ...

    module.exports = {

    ...

    resolve: {

    extensions: ['.js'],

    alias: {

    src: path.resolve(__dirname, './../src')

    }

    }

    ...

    };

  • 通常dev环境和production环境的配置参数比如api domain会有差异,所以需要利用alias将用户两个环境配置文件区分开来
    src目录下新建config目录,src/config目录下新增三个文件:base.js、dev.js、prod.js
    webpack-config/base.js

    1

    2

    3

    export default {

    version: '1.0.0'

    }

    webpack-config/dev.js

    1

    2

    3

    4

    5

    6

    import base from './base'

    export default {

    ...base,

    env: 'dev'

    }

    webpack-config/prod.js

    1

    2

    3

    4

    5

    6

    import base from './base'

    export default {

    ...base,

    env: 'prod'

    }

    配置alias
    webpack-config/dev.js

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    ...

    const path = require('path');

    ...

    module.exports = webpackMerge(base, {

    ...

    resolve: {

    alias: {

    config: path.resolve(__dirname, './../src/config/dev.js')

    }

    }

    ...

    })

    ...

    webpack-config/prod.js

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    ...

    const path = require('path');

    ...

    module.exports = webpackMerge(base, {

    ...

    resolve: {

    alias: {

    config: path.resolve(__dirname, './../src/config/prod.js')

    }

    }

    ...

    })

    ...


以上是关于用webpack构建您的前端项目的主要内容,如果未能解决你的问题,请参考以下文章

webpack入门——构建简易版vue-cli

前端构建工具webpack有啥缺陷

如何使用webpack加载库源映射?

前端自动化构建工具之webpack

深度优化 Webpack 性能,翻倍构建性能

VueJS 和 Webpack:构建项目无法访问 ENV var