mowatermelon / studyNode

Learning record
MIT License
4 stars 1 forks source link

diff-between-webpack-browserify-gulp-grunt- #118

Open mowatermelon opened 6 years ago

mowatermelon commented 6 years ago

  Webpack与Gulp、Grunt没有什么可比性,它可以看作模块打包机,通过分析你的项目结构,找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Scss,TypeScript等),并将其转换和打包为合适的格式供浏览器使用。Gulp/Grunt是一种能够优化前端的开发流程的工具,而WebPack是一种模块化的解决方案,不过Webpack的优点使得Webpack在很多场景下可以替代Gulp/Grunt类的工具。

  他们的工作方式也有较大区别:

  Grunt和Gulp的工作方式是:在一个配置文件中,指明对某些文件进行类似编译,组合,压缩等任务的具体步骤,工具之后可以自动替你完成这些任务。

  Webpack的工作方式是:把你的项目当做一个整体,通过一个给定的主文件(如:index.js),Webpack将从这个文件开始找到你的项目的所有依赖文件,使用loaders处理它们,最后打包为一个(或多个)浏览器可识别的JavaScript文件。

mowatermelon commented 6 years ago

简单的说,Grunt / Gulp 和 browserify / webpack 不是一回事。

1)Gulp / Grunt 可以理解为帮助前端自动化的工具,用于优化前端工作流程。比如自动刷新页面、combo、压缩css、js、编译less等等。二者的区别可以自行百度,个人推荐Gulp。

2)browserify / webpack 提供的是一个前端模块化的方案,和requirejs类似但又有不同。

requirejs是一种在线"编译" 模块的方案,相当于在页面上加载一个AMD 解释器,以便于览器能够识别 define、exports、module,而这些东西就是用于模块化的关键。

而browserify / webpack,则是一个预编译模块的方案。它是预编译的,不需要在浏览器中加载解释器。因此完全可以gulp+webpack 或者gulp+browserify这样的方式搭配使用,甚至,只使用webpack,由于其强大的插件支持,也能满足绝大多数需求了。

mowatermelon commented 6 years ago

相关依赖

  "dependencies": {
    "gulp": "^3.9.1"
  },
  "devDependencies": {
    "gulp": "^3.9.1",
    "gulp-load-plugins": "^1.5.0",
    "gulp-clean": "^0.3.2",
    "gulp-concat": "^2.6.1",
    "gulp-connect": "^5.0.0",
    "gulp-cssmin": "^0.2.0",
    "gulp-imagemin": "^3.3.0",
    "gulp-less": "^3.3.2",
    "gulp-uglify": "^3.0.0",
    "open": "0.0.5"
  }

gulp实战案例

gulpfile.js

    var gulp =require('gulp');
    var $ = require('gulp-load-plugins')();
    var open =  require('open');

    var app ={
        srcPath:'src/',
        devPath:'build/',
        proPath:'dist/'
    }
    gulp.task('lib',function(){
        gulp.src('lib/**/*.min.js')
        .pipe(gulp.dest(app.devPath+'vendor'))
        .pipe(gulp.dest(app.proPath+'vendor'))
        .pipe($.connect.reload());
    });

    gulp.task('html',function(){
        gulp.src(app.srcPath+'**/*.html')
        .pipe(gulp.dest(app.devPath))
        .pipe(gulp.dest(app.proPath))
        .pipe($.connect.reload());
    });

    gulp.task('json',function(){
        gulp.src(app.srcPath+'data/**/*.json')
        .pipe(gulp.dest(app.devPath+'data'))
        .pipe(gulp.dest(app.proPath+'data'))
        .pipe($.connect.reload());
    });

    gulp.task('mdcss',function(){
        gulp.src('lib/mdui/src/mdui.less')
        .pipe($.less())
        .pipe(gulp.dest(app.devPath+'css'))
        .pipe($.cssmin())
        .pipe(gulp.dest(app.proPath+'css'))
        .pipe($.connect.reload());
    });

    gulp.task('mdfont',function(){
        gulp.src('lib/mdui/dist/fonts/**/*')
        .pipe(gulp.dest(app.devPath+'fonts'))
        .pipe(gulp.dest(app.proPath+'fonts'))
        .pipe($.connect.reload());
    });

    gulp.task('mdicon',function(){
        gulp.src('lib/mdui/dist/icons/**/*')
        .pipe(gulp.dest(app.devPath+'fonts'))
        .pipe(gulp.dest(app.proPath+'fonts'))
        .pipe($.connect.reload());
    });

    gulp.task('less',function(){
        gulp.src(app.srcPath+'style/index.less')
        .pipe($.less())
        .pipe(gulp.dest(app.devPath+'css'))
        .pipe($.cssmin())
        .pipe(gulp.dest(app.proPath+'css'))
        .pipe($.connect.reload());
    });

    gulp.task('js',function(){
        gulp.src(app.srcPath+'script/**/*.js')
        .pipe($.concat('index.js'))
        .pipe(gulp.dest(app.devPath+'js'))
        .pipe($.uglify())
        .pipe(gulp.dest(app.proPath+'js'))
        .pipe($.connect.reload());
    });

    gulp.task('image',function(){
        gulp.src(app.srcPath+'image/**/*')
        .pipe(gulp.dest(app.devPath+'image'))
        .pipe($.imagemin())
        .pipe(gulp.dest(app.proPath+'image'))
        .pipe($.connect.reload());
    });

    gulp.task('build',['image','js','mdcss','mdfont','mdicon','less','lib','html','json']);

    gulp.task('serve',['build'],function(){
        $.connect.server({
            root:[app.devPath],
            livereload:true,
            port:1234
        });
        open('http://localhost:1234');

        gulp.watch(app.srcPath+'script/**/*.js',['js']);
        gulp.watch(app.srcPath+'lib/**/*.js',['lib']);
        gulp.watch(app.srcPath+'**/*.html',['html']);
        gulp.watch(app.srcPath+'data/**/*.json',['json']);
        gulp.watch(app.srcPath+'lib/mdui/src/mdui.less',['mdcss']);
        gulp.watch(app.srcPath+'lib/mdui/dist/fonts/**/*',['mdfont']);
        gulp.watch(app.srcPath+'lib/mdui/dist/icons/**/*',['mdicon']);
        gulp.watch(app.srcPath+'style/index.less',['less']);
        gulp.watch(app.srcPath+'image/**/*',['image']);
    });

    gulp.task('default',['serve']);//输入gulp默认执行的命令

    gulp.task('clean',function(){
        gulp.src([app.devPath,app.proPath])
        .pipe($.clean());
    });
mowatermelon commented 6 years ago

jQuery在使用grunt,bootstrap在使用grunt,百度UEditor在使用grunt

gruntLogo

cnpm i grunt-cli -g

安装成功之后,在项目中新建Gruntfile.js 文件,注意文件首字母大写

基础案例

base

运行案例

run-base

安装相关依赖包

请注意按需引用

actually-use-dependent-package

引用相关包

grunt.loadNpmTasks(NpmPackageName)

// 举个例子
grunt.loadNpmTasks('grunt-contrib-watch')

对引入包进行相关配置

NpmPackageName :{
      prop1:prop1Value,
      prop2:prop2Value
}

// 举个例子
watch:{
    build:{
        files:['src/*.js','src/*.css'],
        tasks:['jshint','uglify'],
        options:{spawn:false}
    }
}

再执行使用

grunt.registerTask('default',[NpmPackageName1,NpmPackageName2,NpmPackageName3,....])

// 举个例子
grunt.registerTask('default',['jshint','uglify','watch'])

放上完整代码

all-code

关于build属性

那么这里是不是必须用“build”这一个名字?答案很明显,当然不是。之前之所以没直接说,是因为要先把插件的安装和配置讲明白,不变一次传输太多知识,现在一并和大家说了。

这里可以用任何字符串代替“build”(但要符合js语法规则)。甚至,你可以把“build”指向的内容分开来写。这样对多人协同开发很友好。好的设计就是这样:让用户尽情发挥他们的自由来干事,而不是去限制他们。

with-no-build-prop

mowatermelon commented 6 years ago
mowatermelon commented 6 years ago

根据自己的需要和项目的特点来取舍吧

grunt是前端工程化的先驱,是yeoman重要的一环,跟gulp类似都是任务执行器,只是gulp提供了更自然基于流的方式连接不同的任务。作为任务执行器理论上可以做跟自动化相关的任何事情,只是在前端用的多些,而且现成的工具也比较多。

webpack更专注于前端的通用解决方案,定义了前端构建的标准模式,同时提供了良好的扩展机制,社区发现良好。

国内百度也有一套开源的解决方案,叫fis,个人感觉语法比较清晰,也很方便定制,通过生成资源表和提供的加载框架在做细粒度的优化上比webpack灵活些(毕竟是国内的,社区就没有webpack那么活跃了,主要是百度的工程师在维护)。

总体对于单纯前端构建来说,小项目差别不大,什么熟用什么吧,大项目用webpack和fis这种后期维护会省力很多

mowatermelon commented 6 years ago

gulp和webpack两者功能有重复,但不是完全的替代关系,可以混用的.

mowatermelon commented 6 years ago

这几样除了 Grunt,你都搞懂了就知道该在哪些场景用用什么最合适了。Grunt 适合那些非 Node 程序员或者从其他语言转过来的开发者。

这些工具我只能说各有特点,我在不同类型的项目中都用过。但是基于本能我会对那些过于“功能丰富”的抱有戒心,例如 Webpack。还有一些强迫症认为 npm scripts 就够了,增加对 gulp 的依赖没必要,这也不是绝对的。

因为稍微复杂一些的控制还是要写 Shell 或者其他 JS 程序来帮助你,否则更麻烦。不过,考虑到实际工作中要创建大量的实验项目,那么用 Gulp 更多,前端会和 Browserify 配合。

但是写 React-Native 的时候往往会再配合 Webpack(让 React Native 更多地利用 Node.JS 的资产),因为 Browserify 在这块还没人研究好。

最近写了好几个 Yeoman Generator 来加速实验项目的创建,生成的脚手架也是以 Gulp 为主。

还有一个最重要的,Gulp 是基于 Node Stream 创建的,Node Stream 是 Node.JS 中及其重要的设计模式,天生就具有良好的互操作性,这是 Webpack 比不了的(但同时也是很多不熟悉 Node Stream 的人不愿意用 Gulp 的原因)。

而 Webpack 的优势在于对于复杂的依赖树进行重新批分,以便于前端多页面的复用,这是唯一的优势。但是其自身的 Require 实现也是对目前 Node.JS 的模块系统的一种污染。

mowatermelon commented 6 years ago

用过gulp,最近在琢磨webpack。

感觉两者理念不太同:gulp是将一系列常规的task按部就班的自动执行,比如scripts和less各自的compile-minify-transfer等等,它从使用者的角度出发,做的还是那些大家手动会去做的事,只不过自动化了,通过一个脚本来执行,grunt类似;

而webpack则把scripts和static files均视为module,通过loader(比如babel-loader\less-loader,分别用以把es6转为es5和less转为css)的处理,将之转化为项目所需的所谓的assets。

它从项目的角度出发,通过配置(webpack.config.js)实现,因此更加省心(理论上,因为不用考虑脚本的逻辑,当然,看起来可配置的项还是挺复杂的,不过这些都是死的吧,而且常用的就那几项)。

webpack吓我一大蹦的地方在于,一执行命令,就只需引入一个bundle.js,样式脚本啥啥啥的都有了,哭。

mowatermelon commented 6 years ago

gulp 和 grunt 都有的扩展包

clean 清空文件 文件夹 (grunt-contrib-clean和 gulp-clean) concat 文件,文件夹合并 (grunt-contrib-concat和 gulp-concat)

uglify 压缩js(grunt-contrib-uglify和 gulp-uglify)

gulp独有

gulp-load-plugins 加载gulp的各种扩展包,不用像grunt那样需要自己一个一个写扩展包引用,这边可以直接通过这个配置插件,直接就加载对应扩展包,就可以进行使用了

    var gulp =require('gulp');
    var $ = require('gulp-load-plugins')();//引入配置包的时候注意要在最后写一个括号,表示直接执行

    var app ={
        srcPath:'src/',
        devPath:'build/',
        proPath:'dist/'
    }
    gulp.task('lib',function(){
        gulp.src('lib/**/*.min.js')
        .pipe(gulp.dest(app.devPath+'vendor'))
        .pipe(gulp.dest(app.proPath+'vendor'))
        .pipe($.connect.reload());//这样可以直接就通过$.NpmPackageName就可以调用了
    });

cssmin 压缩css (gulp-cssmin)

imagemin 压缩图片 (gulp-imagemin)

less 函数css (gulp-less)

grunt独有

copy 复制文件,文件夹 (grunt-contrib-copy ),有点类似gulp中的gulp.dest,对重新编译之后的文件生成到制定文件夹

watch 监听文件变化 (grunt-contrib-watch )


// 引用相关包
grunt.loadNpmTasks('grunt-contrib-watch')

// 对引入包进行相关配置
watch:{
    build:{
        files:['src/*.js','src/*.css'],
        tasks:['jshint','uglify'],
        options:{spawn:false}
    }
}

//再执行使用
grunt.registerTask('default',['watch'])
mowatermelon commented 6 years ago

总结来说

webpack , browserify , rollup是一类,主攻javascript模块打包方案(方案+工具+插件),webpack更专注于前端的通用解决方案,定义了前端构建的标准模式,同时提供了良好的扩展机制,社区发现良好。

但是grunt , gulp , 主攻前端工具,结合插件,合并、压缩、编译 sass/less,browser 自动载入资源,grunt是前端工程化的先驱,是yeoman重要的一环,跟gulp类似都是任务执行器,只是gulp提供了更自然基于流的方式连接不同的任务。作为任务执行器理论上可以做跟自动化相关的任何事情,只是在前端用的多些,而且现成的工具也比较多。

mowatermelon commented 6 years ago

https://blog.csdn.net/u014168594/article/details/77198729

浅谈webpack打包原理

模块化机制

webpack并不强制你使用某种模块化方案,而是通过兼容所有模块化方案让你无痛接入项目。有了webpack,你可以随意选择你喜欢的模块化方案,至于怎么处理模块之间的依赖关系及如何按需打包,webpack会帮你处理好的。

关于模块化的一些内容,可以看看我之前的文章:js的模块化进程

核心思想

文件管理

打包原理

把所有依赖打包成一个bundle.js文件,通过代码分割成单元片段并按需加载。

bundle.js

如图,entry.js是入口文件,调用了util1.js和util2.js,而util1.js又调用了util2.js。

打包后的bundle.js例子

/******/ ([
/* 0 */     //模块id
/***/ function(module, exports, __webpack_require__) {

    __webpack_require__(1);     //require资源文件id
    __webpack_require__(2);

/***/ },
/* 1 */
/***/ function(module, exports, __webpack_require__) {
    //util1.js文件
    __webpack_require__(2);
    var util1=1;
    exports.util1=util1;

/***/ },
/* 2 */
/***/ function(module, exports) {
    //util2.js文件
    var util2=1;
    exports.util2=util2;

/***/ }
...
...
/******/ ]);
mowatermelon commented 6 years ago

承担了很多前端代码的自动化生成的工作。 搭配很多loader,可以完成不同的事情。 比如说把繁杂的项目中用到的js打包并压缩成你设定的一个或多个文件去发布到生产环境。 比如把react中引用的图片压缩并抽出来项目中引用到的。没引用到的,不会拿过来。 比如CSS Modules,单个组件开发时用单独的css定义,最终打包成一个css文件里,而没用到的css不会打包。而且通过设置,不用担心各个文件中的同名css打包到一个css里后重复。 less转css sass转css PostCSS转css es6写的js,通过babel转成浏览器可执行的es5 ......

原理,其实就是用程序代替人的工作。比如我们开发的时候经常是css写到一个或几个文件,随着迭代,里面肯定就产生很多垃圾不再用的代码,而用人去识别很累,如果用CSS Modules,把css也模块化,随着迭代,不再引用到的,就不会再打包进去,不用人来识别了。同理,浏览器是不识别sass这种的,程序来帮你转成浏览器可读的css。 不管用什么程序,其实都可以开发这样的一个解释器,其实就是一个翻译转化的过程。只不过当前前端的发展需要和node的便捷加上npm方便的分发,所以在node中,大量的工具出现了。

其实原理和功能,就是用程序代替人做那些事,由程序翻译成普通浏览器可执行的代码。

mowatermelon commented 6 years ago

原理: 一切皆为模块,由于webpack并不支持除.js以外的文件,从而需要使用loader转换成webpack支持的模块,plugin用于扩展webpack的功能,在webpack构建生命周期的过程在合适的时机做了合适的事情。

先了解一下Webpack从构建到输出文件结果的过程: 1.解析配置参数,合并从shell传入和webpack.config.js文件的配置信息,输出最终的配置信息 2.注册配置中的插件,好让插件监听webpack构建生命周期中的事件节点,做出对应的反应 3.解析配置文件中entry入口文件,并找出每个文件依赖的文件,递归下去 4.在递归每个文件的过程中,根据文件类型和配置文件中loader找出相对应的loader对文件进行转换 5.递归结束之后得到每个文件最终的结果,根据entry配置生成代码chunk 6.输出所有chunk到文件系统

mowatermelon commented 6 years ago

放上一个正在使用的webpack

webpack.base.conf.js

var path = require('path')
var utils = require('./utils')
var config = require('../config')
var vueLoaderConfig = require('./vue-loader.conf')

function resolve (dir) {
  return path.join(__dirname, '..', dir)
}

module.exports = {
  entry: {
    app: './src/main.js'
  },
  output: {
    path: config.build.assetsRoot,
    filename: '[name].js',
    publicPath: process.env.NODE_ENV === 'production'
      ? config.build.assetsPublicPath
      : config.dev.assetsPublicPath
  },
  resolve: {
    extensions: ['.js', '.vue', '.json'],
    alias: {
      'vue$': 'vue/dist/vue.esm.js',
      '@': resolve('src')
    }
  },
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: vueLoaderConfig
      },
      {
        test: /\.js$/,
        loader: 'babel-loader',
        include: [resolve('src'), resolve('test')]
      },
      {
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('img/[name].[hash:7].[ext]')
        }
      },
      {
        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
        }
      }
    ]
  }
}

webpack.dev.conf.js

var utils = require('./utils')
var webpack = require('webpack')
var config = require('../config')
var merge = require('webpack-merge')
var baseWebpackConfig = require('./webpack.base.conf')
var HtmlWebpackPlugin = require('html-webpack-plugin')
var FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')

// add hot-reload related code to entry chunks
Object.keys(baseWebpackConfig.entry).forEach(function (name) {
  baseWebpackConfig.entry[name] = ['./build/dev-client'].concat(baseWebpackConfig.entry[name])
})

module.exports = merge(baseWebpackConfig, {
  module: {
    rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap })
  },
  // cheap-module-eval-source-map is faster for development
  devtool: '#cheap-module-eval-source-map',
  plugins: [
    new webpack.DefinePlugin({
      'process.env': config.dev.env
    }),
    // https://github.com/glenjamin/webpack-hot-middleware#installation--usage
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoEmitOnErrorsPlugin(),
    // https://github.com/ampedandwired/html-webpack-plugin
    new HtmlWebpackPlugin({
      filename: 'index.html',
      template: 'index.html',
      inject: true
    }),
    new FriendlyErrorsPlugin()
  ]
})

webpack.prod.conf.js

var path = require('path')
var utils = require('./utils')
var webpack = require('webpack')
var config = require('../config')
var merge = require('webpack-merge')
var baseWebpackConfig = require('./webpack.base.conf')
var CopyWebpackPlugin = require('copy-webpack-plugin')
var HtmlWebpackPlugin = require('html-webpack-plugin')
var ExtractTextPlugin = require('extract-text-webpack-plugin')
var OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')

var env = process.env.NODE_ENV === 'testing'
  ? require('../config/test.env')
  : config.build.env

var webpackConfig = merge(baseWebpackConfig, {
  module: {
    rules: utils.styleLoaders({
      sourceMap: config.build.productionSourceMap,
      extract: true
    })
  },
  devtool: config.build.productionSourceMap ? '#source-map' : false,
  output: {
    path: config.build.assetsRoot,
    filename: utils.assetsPath('js/[name].[chunkhash].js'),
    chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
  },
  plugins: [
    // http://vuejs.github.io/vue-loader/en/workflow/production.html
    new webpack.DefinePlugin({
      'process.env': env
    }),
    new webpack.optimize.UglifyJsPlugin({
      compress: {
        warnings: false
      },
      sourceMap: true
    }),
    // extract css into its own file
    new ExtractTextPlugin({
      filename: utils.assetsPath('css/[name].[contenthash].css')
    }),
    // Compress extracted CSS. We are using this plugin so that possible
    // duplicated CSS from different components can be deduped.
    new OptimizeCSSPlugin({
      cssProcessorOptions: {
        safe: true
      }
    }),
    // generate dist index.html with correct asset hash for caching.
    // you can customize output by editing /index.html
    // see https://github.com/ampedandwired/html-webpack-plugin
    new HtmlWebpackPlugin({
      filename: process.env.NODE_ENV === 'testing'
        ? 'index.html'
        : config.build.index,
      template: 'index.html',
      inject: true,
      minify: {
        removeComments: true,
        collapseWhitespace: true,
        removeAttributeQuotes: true
        // more options:
        // https://github.com/kangax/html-minifier#options-quick-reference
      },
      // necessary to consistently work with multiple chunks via CommonsChunkPlugin
      chunksSortMode: 'dependency'
    }),
    // split vendor js into its own file
    new webpack.optimize.CommonsChunkPlugin({
      name: 'vendor',
      minChunks: function (module, count) {
        // any required modules inside node_modules are extracted to vendor
        return (
          module.resource &&
          /\.js$/.test(module.resource) &&
          module.resource.indexOf(
            path.join(__dirname, '../node_modules')
          ) === 0
        )
      }
    }),
    // extract webpack runtime and module manifest to its own file in order to
    // prevent vendor hash from being updated whenever app bundle is updated
    new webpack.optimize.CommonsChunkPlugin({
      name: 'manifest',
      chunks: ['vendor']
    }),
    // copy custom static assets
    new CopyWebpackPlugin([
      {
        from: path.resolve(__dirname, '../static'),
        to: config.build.assetsSubDirectory,
        ignore: ['.*']
      }
    ])
  ]
})

if (config.build.productionGzip) {
  var CompressionWebpackPlugin = require('compression-webpack-plugin')

  webpackConfig.plugins.push(
    new CompressionWebpackPlugin({
      asset: '[path].gz[query]',
      algorithm: 'gzip',
      test: new RegExp(
        '\\.(' +
        config.build.productionGzipExtensions.join('|') +
        ')$'
      ),
      threshold: 10240,
      minRatio: 0.8
    })
  )
}

if (config.build.bundleAnalyzerReport) {
  var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
  webpackConfig.plugins.push(new BundleAnalyzerPlugin())
}

module.exports = webpackConfig

webpack.test.conf.js

// This is the webpack config used for unit tests.

var utils = require('./utils')
var webpack = require('webpack')
var merge = require('webpack-merge')
var baseConfig = require('./webpack.base.conf')

var webpackConfig = merge(baseConfig, {
  // use inline sourcemap for karma-sourcemap-loader
  module: {
    rules: utils.styleLoaders()
  },
  devtool: '#inline-source-map',
  plugins: [
    new webpack.DefinePlugin({
      'process.env': require('../config/test.env')
    })
  ]
})

// no need for app entry during tests
delete webpackConfig.entry

module.exports = webpackConfig
mowatermelon commented 6 years ago

webpack-dev-server

webpack-dev-server在webpack的watch基础上开启服务器。

webpack-dev-server是运行在内存中的开发服务器,支持高级webpack特性hot module replacement。这对于react vue这种组件化开发是很方便的。

使用webpack-dev-server命令开启服务器,配合HMR及可以实现代码更改浏览器局部刷新的能力。

hot module replacement

Hot Module Replacement (HMR) exchanges, adds, or removes modules while an application is running without a page reload.

当应用在运行期间hmr机制能够修改、添加、或者移除相应的模块,而不使整个页面刷新。 hmr机制适用于单页应用。

要实现hmr机制,需要配合webpack-dev-server服务器,这个服务器本身就实现了监察watch文件改动的能力,再开启HMR选项,就添加了watch模块变化的能力。这是HMR机制能生效的基础。

从webpack编译器角度

每次修改一个模块的时候,webpack会生成两部分,一个是manifest.json,另一部分是关于这次模块更新编译完成的chunks。manifest.json中存着的是chunk更改前后的hash值。

从编译器webpack的角度来讲提供了hmr的原材料。供后续使用。

从模块的角度

模块发生变化时,webpack会生成之前讲过的两部分基础文件,但是何时将变化后的模块应用到app中去?这里就需要在应用代码中编写handler去接受到模块变化信息。但是不能在所有模块中编写handler吧?这里就用到了消息冒泡机制。

Scalable webpack configurations

在单个配置文件中维护配置,但是区分好条件分支。调用不同的npm命令时候设置不同的环境变量,然后在分支中匹配,返回我们需要的配置文件。

这样做的好处可以在一个文件中管理不同npm操作的逻辑,并且可以共用相同的配置。webpack-merge这个模块可以起到合并配置的作用。

dep-bubble