• 详解前端模块化工具-webpack


    webpack是一个module bundler,抛开博大精深的汉字问题,我们暂且管他叫'模块管理工具'。随着js能做的事情越来越多,浏览器、服务器,js似乎无处不在,这时,使日渐增多的js代码变得合理有序就显得尤为必要,也应运而生了很多模块化工具。从服务器端到浏览器端,从原生的没有模块系统的`<script>`到基于Commonjs和AMD规范的实现到ES6 modules。为了模块化和更好的模块化,我们总是走在探索的路上。

    但是这些实现模块化的方法或多或少都有他们的缺点。比如说使用`<script>`标签导入js模块,顺序不好把握且我们需要自己梳理可能的冲突和依赖;使用Commonjs规范来解决问题,它使我们在服务器端的模块得到了重用,但是在浏览器端,网络的请求都是异步的,无法并行的require多个module。ES6 modules的实现也只是一小部分,并且想要得到所有浏览器的支持,相比还是需要相当的一段时间。

    当然灵活的模块管理只是webpack的众多特性之一,它还有众多优秀的特性:

    1 - 它同时支持commonjs和AMD规范(甚至混合的形式);
    2 - 它可以打成一个完整的包,也可以分成多个部分,在运行时异步加载(可以减少第一次加载的时间);
    3 - 依赖在编译时即处理完毕,可以减少运行时包的大小;
    4 - Loaders可以使文件在编译时得到预处理,这可以帮我们做很多事情,比如说模板的预编译,图片的base64处理;
    5 - 丰富的和可扩展的插件可以适应多变的需求。

    一、webpack详解

    最近在学习ES6和React相关的知识,为了更加方便自己写代码,给自己写了个脚手架generator-reactpack。生成的项目中可以看到一个基本的webpack.config.js文件:

     1 var webpack = require('webpack');
     2 module.exports = {
     3     entry: [
     4         'webpack/hot/only-dev-server',
     5         './js/app.js'
     6     ],
     7     output: {
     8         path: './build',
     9         filename: 'bundle.js'
    10     },
    11     module: {
    12         loaders: [
    13         { test: /.js?$/, loaders: ['react-hot', 'babel'], exclude:     /node_modules/ },
    14         { test: /.js$/, exclude: /node_modules/, loader: 'babel-loader'},
    15         { test: /.css$/, loader: "style!css" },
    16         {test: /.less/,loader: 'style-loader!css-loader!less-loader'}
    17         ]
    18     },
    19     resolve:{
    20         extensions:['','.js','.json']
    21     },
    22     plugins: [
    23         new webpack.NoErrorsPlugin()
    24     ]
    25 };                        

    webpack.config.js文件通常放在项目的根目录中,它本身也是一个标准的Commonjs规范的模块。在导出的配置对象中有几个关键的参数:

    1.entry

    entry可以是个字符串或数组或者是对象。

    当entry是个字符串的时候,用来定义入口文件:

    1 entry: './js/main.js'

    当entry是个数组的时候,里面同样包含入口js文件,另外一个参数可以是用来配置webpack提供的一个静态资源服务器,webpack-dev-server。webpack-dev-server会监控项目中每一个文件的变化,实时的进行构建,并且自动刷新页面:

    1 entry: [
    2     'webpack/hot/only-dev-server',
    3     './js/app.js'
    4 ]

    当entry是个对象的时候,我们可以将不同的文件构建成不同的文件,按需使用,比如在我的hello页面中只要<script src='build/Profile.js'></script>引入hello.js即可:

    1 entry: {
    2     hello: './js/hello.js',
    3     form: './js/form.js'
    4 }

    2.output

    output参数是个对象,用于定义构建后的文件的输出。其中包含path和filename:

    1 output: {
    2     path: './build',
    3     filename: 'bundle.js'
    4 }

    当我们在entry中定义构建多个文件时,filename可以对应的更改为[name].js用于定义不同文件构建后的名字。

    3.module

    关于模块的加载相关,我们就定义在module.loaders中。这里通过正则表达式去匹配不同后缀的文件名,然后给它们定义不同的加载器。比如说给less文件定义串联的三个加载器(!用来定义级联关系):

    1 module: {
    2     loaders: [
    3         { test: /.js?$/, loaders: ['react-hot', 'babel'], exclude: /node_modules/ },
    4         { test: /.js$/, exclude: /node_modules/, loader: 'babel-loader'},
    5         { test: /.css$/, loader: "style!css" },
    6         { test: /.less/, loader: 'style-loader!css-loader!less-loader'}
    7     ]
    8 }

    此外,还可以添加用来定义png、jpg这样的图片资源在小于10k时自动处理为base64图片的加载器:

    1 { test: /.(png|jpg)$/,loader: 'url-loader?limit=10000'}

    给css和less还有图片添加了loader之后,我们不仅可以像在node中那样require js文件了,我们还可以require css、less甚至图片文件:

    1 require('./bootstrap.css');
    2 require('./myapp.less');
    3 var img = document.createElement('img');
    4 img.src = require('./glyph.png');

    但是需要知道的是,这样require来的文件会内联到 js bundle中。如果我们需要把保留require的写法又想把css文件单独拿出来,可以使用下面提到的[extract-text-webpack-plugin]插件。

    在上面示例代码中配置的第一个loaders我们可以看到一个叫做react-hot的加载器。我的项目是用来学习react写相关代码的,所以配置了一个react-hot加载器,通过它,可以实现对react组件的热替换。我们已经在entry参数中配置了`webpack/hot/only-dev-server`,所以我们只要在启动webpack开发服务器时开启--hot参数,就可以使用react-hot-loader了。在package.json文件中这样定义:

    1 "scripts": {
    2     "start": "webpack-dev-server --hot --progress --colors",
    3     "build": "webpack --progress --colors"
    4 }

    4.resolve

    webpack在构建包的时候会按目录的进行文件的查找,resolve属性中的extensions数组中用于配置程序可以自行补全哪些文件后缀:

    1 resolve:{
    2     extensions:['','.js','.json']
    3 }

    然后我们想要加载一个js文件时,只要require('common')就可以加载common.js文件了。


    5.plugin

    webpack提供了[丰富的组件]用来满足不同的需求,当然我们也可以自行实现一个组件来满足自己的需求。在我的项目里面没有特殊的需求,于是便只是配置了NoErrorsPlugin插件,用来跳过编译时出错的代码并记录,使编译后运行时的包不会发生错误:

    1 plugins: [
    2     new webpack.NoErrorsPlugin()
    3 ]


    6.externals

    当我们想在项目中require一些其他的类库或者API,而又不想让这些类库的源码被构建到运行时文件中,这在实际开发中很有必要。此时我们就可以通过配置externals参数来解决这个问题:

    1 externals: {
    2     "jquery": "jQuery"
    3 }

    这样我们就可以放心的在项目中使用这些API了:var jQuery = require("jquery");

    7.context

    当我们在require一个模块的时候,如果在require中包含变量,像这样:

    1 require("./mods/" + name + ".js");


    那么在编译的时候我们是不能知道具体的模块的。但这个时候,webpack也会为我们做些分析工作:

    1.分析目录:'./mods';
    2.提取正则表达式:'/^.*.js$/';

    于是这个时候为了更好地配合wenpack进行编译,我们可以给它指明路径,像在cake-webpack-config中所做的那样(我们在这里先忽略abcoption的作用):

    1 var currentBase = process.cwd();
    2 var context = abcOptions.options.context ? abcOptions.options.context : 
    3 path.isAbsolute(entryDir) ? entryDir : path.join(currentBase, entryDir);

    二、why webpack?

    webpack与gulp并不矛盾,甚至一起使用会得到最大化的利益,webpack与gulp。使用webpack进行assets编译,使用gulp进行打包似乎就是为了让它们各司其职,用其所长。

    关于工具的定位

    webpack的定位是module bundler,作为模块化工具,它的竞争对手看起来更像是[browserify],而不是[Gulp],基于流的自动化构建工具。然而事实也是这样的。  

    功能和使用方式上的不同

    webpack提供了一些非常实用的功能,像在前面我们体会到的那些,比如说图片的处理,resolve的处理,分开构建[webpack-code-splitting]等等 。 许多其他工具需要插件的功能,webpack只要配置一下就可以了。

    有时候一个东西到底怎样,自己折腾一遍或许体会就更加深刻了。

    三、拓展阅读

    - [webpack]
    - [cake-webpack-config]
    - [webpack-howto]
    - [webpack-compared]

  • 相关阅读:
    系统架构得11个原则
    深圳变更企业法人
    Mixpanel可以做些什么?
    jira tikcet 过滤版本
    Math.random().toString(36)使用 随机生成数字与字母的组合
    javascript多个等号含义
    登陆dev,AWS需要sso,ip到白名单
    MobSDK已停止支持非严格模式版本,请按上面编译告示接入合规版本!
    thinkphp 变量修饰符
    wsl 关闭 命令
  • 原文地址:https://www.cnblogs.com/skylar/p/webpack-module-bundler.html
Copyright © 2020-2023  润新知