HolyZheng / holyZheng-blog

草稿
36 stars 0 forks source link

webpack与gulp #11

Open HolyZheng opened 6 years ago

HolyZheng commented 6 years ago

gulp

是什么

gulp是一个自动化构建工具,用来优化前端工作流程,可以帮助我们进行代码编译,压缩,图片压缩,资源合并等工作。

webpack

是什么

webpack是一个模块化资源打包工具,它支持多种模块化的方案,当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。并且通过对应的 loader ,我们还可以处理其他类型的文件,比如css,image。通过plugins还可以对代码进行压缩等操作。

ps:loader 类似于其他构建工具中“任务(task)”,用于对模块的源代码进行转换,可以将文件从不同的语言(如 TypeScript)转换为 JavaScript,或将内联图像转换为 data URL等等。

使用场景

用途就决定了它的使用场景,当我们想简化工作流程,自动化代码的压缩,编译,图片压缩,资源合并等操作,gulp可以排上用场。 如果是进行模块化开发,并根据模块依赖关系打包代码和资源的话,webpack可以排上用场,并且webpack通过plugins还可以做额外的事情。

HolyZheng commented 6 years ago

webpack4总结

接触的时候已经到了webpack4版本。webpack4的改变:参考dwqs同学的webpack4不完全指南

关键概念

webpack中的关键概念

/** webpack.production.config.js **/
   // webpack 2/3 
   module.exports = {
       plugins: [
        new UglifyJsPlugin(/* ... */),
        new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("production") }),
        new webpack.optimize.ModuleConcatenationPlugin(),
/*
* 分析出模块之间的依赖关系,尽可能的把打散的模块合并到一个函数中去,但前提是不能造成代码冗
* 余。 因此只有那些被引用了一次的模块才能被合并。
*/
        new webpack.NoEmitOnErrorsPlugin()
// 在编译出现错误时,使用它来跳过输出阶段。这样可以确保输出资源不会包含错误。
       ]
     }

   // webpack 4  
   module.exports = {
    mode: 'production'
   }
   /** webpack.development.config.js **/
   // webpack 2/3 
   module.exports = {
       plugins: [
        new webpack.NamedModulesPlugin(), 
// 当开启 HMR 的时候使用该插件会显示模块的相对路径,建议用于开发环境
        new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("development") })
       ]
     }

   // webpack 4  
   module.exports = {
    mode: 'development'
   }

webpack4优化点

1.模块热替换

允许在运行时更新各种模块,而无需进行完全刷新。

const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
// 每次打包创建新的html
const CleanWebpackPlugin = require('clean-webpack-plugin')
// 每次打包先清空dist文件夹
const webpack = require('webpack')

module.exports = {
  entry: {
    app: './src/index.js'
  },
  // 追踪错误,仅在开发阶段使用
  devtool: 'inline-source-map',
  plugins: [
    new CleanWebpackPlugin(['dist']),
    new HtmlWebpackPlugin({
      title: 'Development'
    }),
    // new webpack.NamedModulesPlugin(), 生成环境下默认开启
    // 以便更容易查看要修补(patch)的依赖
    new webpack.HotModuleReplacementPlugin()
    // 热替换模块
  ],
  mode: 'development',
  output: {
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, 'dist')
  },
// 将dist目录下的文件作为可访问文件,在package.json中添加script脚本
// "start": "webpack-dev-server --open"
  devServer: {
    contentBase: './dist',
    hot: true
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
        // style-loader当更新 CSS 依赖模块时,此 loader 在后台使用 module.hot.accept 来修补(patch) 
        // <style> 标签
      }
    ]
  }
}

2.tree shaking

tree shaking 是一个术语,通常用于描述移除 JavaScript 上下文中的未引用代码(dead-code)。它依赖于 ES2015 模块系统中的静态结构特性,例如 import 和 export。我们需要做的就是将文件标记为无副作用,或者将有副作用的文件标记出来。

{ "name": "your-project", "sideEffects": [ "./src/some-side-effectful-file.js", "*.css" ] // 列出有副作用的文件,避免进行tree shaking }

> ps:副作用」的定义是,在导入时会执行特殊行为的代码,而不是仅仅暴露一个 export 或多个 export。举例说明,例如 polyfill,它影响全局作用域,并且通常不提供 export。
### 3. 生成环境搭建
 针对不同的环境,进行不同的配置,用webpage-merge进行合并。
如:
```js
// webpack.common.js

const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const CleanWebpackPlugin = require('clean-webpack-plugin')

module.exports = {
  entry: {
    app: './src/index.js'
  },
  plugins: [
    new CleanWebpackPlugin(['dist']),
    new HtmlWebpackPlugin({
      title: 'production'
    })
  ],
  output: {
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, 'dist')
  }
}
// webpack.dev.js

const merge = require('webpack-merge')
const common = require('./webpack.common.js')

module.exports = merge(common, {
  devtool: 'inline-source-map',
  devServer: {
    contentBase: './dist'
  }
})
// webpack.prod.js

const merge = require('webpack-merge')
const UglifyJSPlugin = require('uglifyjs-webpack-plugin')
const common = require('./webpack.common.js')
const webpack = require('webpack')

module.exports = merge(common, {
  devtool: 'source-map',
  plugins: [
    // new UglifyJSPlugin({
    // sourceMap: true
    //}),
    // 在webpack4的production模式下,webpack.DefinePlugin 插件的 
    // process.env.NODE_ENV 的值不需要再定义,默认是 production
    // new webpack.DefinePlugin({
     //  'process.env.NODE_ENV': JSON.stringify('production')
    // })
    // webpack中需要通过defineplugin来设置process.env.NODE_ENV的值,
    // 以便其他的library使用该变量
  ],
  mode: 'production'
})

4.公共代码提取,常用代码提取,webpack的runtime代码提取

参考文章:RIP CommonsChunkPlugin 推荐参考翻译:RIP CommonsChunkPlugin翻译 webpack4移除了CommonsChunkPlugin插件,增加了optimization.splitchunksoptimization.runtimeChunk配置项,这两个配置项的默认配置已经可以满足大部分用户的需求。

“async initial all”的区别

// 默认配置
splitChunks: {
    chunks: "async",
    minSize: 30000,
    minChunks: 1,
    maxAsyncRequests: 5,
    maxInitialRequests: 3,
    name: true,
    cacheGroups: {
        default: {
            minChunks: 2,
            priority: -20,
            reuseExistingChunk: true,
        },
        vendors: {
            test: /[\\/]node_modules[\\/]/,
            priority: -10
        }
    }
}

weback3的配置

new webpack.optimize.CommonsChunkPlugin({
      name: "common",
      minChunks: 2,
      minSize: 30000
})

new webpack.optimize.CommonsChunkPlugin({
  name: 'vendor',
  minChunks: module => {
    return module.resource && /node_modules/.test(module.resource)
  }
}),
//  webpack 自身的这部分代码分离出来
new webpack.optimize.CommonsChunkPlugin({
      name: "runtime",
      minChunks: Infinity
})

5.懒加载

6.缓存

为了避免由于文件名字没有改变,浏览器误以为我们文件的内容没有改变,直接使用内存中的缓存,而造成不必要的后果,配置chunkhash

output: {
    filename: '[name].[chunkhash].bundle.js',
    path: path.resolve(__dirname, 'dist')
  }

使得每次修改都会产生一个唯一的hash后缀,以区别文件名,使得浏览器可以使用到最新版本的代码。 wbepack4之前,会因为webpack自身的样板和mainifest,会造成即使没有修改代码,但是再次打包出来的代码名称中的hash却改变的问题,需要通过插件来提取出来。

// webpack4中取消了该插件
new webpack.optimize.CommonsChunkPlugin({
      name: "runtime",
      minChunks: Infinity
})
// webpack4
optimization.runtimeChunk: true

vue环境配置

用到vue-loadervue-style-loader

module: {
    // .vue文件中使用css需要css-loader
    // vue-style-loader是vue-loader的dependencies
    // css-loader 是 vue-loader的peerDependencies
    rules: [
     {
        test: /\.js$/,
        use: 'babel-loader',
        include: [path.resolve(__dirname, '../src')]
      },
      {
        test: /\.vue$/,
        use: ['vue-loader']
      },
      {
        test: /\.css$/,
        use: ['vue-style-loader', 'css-loader']
      },
      {
        test: /\.s[ac]ss$/,
        use: ['vue-style-loader', 'css-loader', 'sass-loader']
      }
    ]
  }
HolyZheng commented 6 years ago

gulp

gulp的配置文件为 gulpfile.js 其中有几个关键和 api 的概念:

Task任务

gulp的配置可以说是由一个个的任务组成,每个任务都由一个名字(自定义或者default),可以通过命令指定要执行的任务。

// 执行default任务
gulp 
// 执行特点任务
gulp name

插件

任务的完成通常需要插件的帮助,常用插件有:

常用Api

gulp.task('default', ['script'], function () { gulp.watch('./src/js/*.js', ['script']); });

### 执行顺序
gulp中的任务,在默认情况下,会·已最大的并发数同时运行,也就是说,它会不做任何等待地将所有的任务同时开起来。如果你希望创建一个有特定顺序的**串行**的任务链,你需要做两件事:
1. 给它一个提示,用以告知任务在什么时候完成,
2. 而后,再给一个提示,用以告知某任务需要依赖另一个任务的完成。
官方两个例子:
```js
var gulp = require('gulp');

// 传入一个回调函数,因此引擎可以知道何时它会被完成
gulp.task('one', function(cb) {
    // 做一些事 -- 异步的或者其他任何的事
    cb(err); // 如果 err 不是 null 和 undefined,流程会被结束掉,'two' 不会被执行
});

// 标注一个依赖,依赖的任务必须在这个任务开始之前被完成
gulp.task('two', ['one'], function() {
    // 现在任务 'one' 已经完成了
});

gulp.task('default', ['one', 'two']);
// 也可以这么写:gulp.task('default', ['two']);
var gulp = require('gulp');
var del = require('del'); // rm -rf

gulp.task('clean', function(cb) {
    del(['output'], cb);
});

gulp.task('templates', ['clean'], function() {
    var stream = gulp.src(['src/templates/*.hbs'])
        // 执行拼接,压缩,等。
        .pipe(gulp.dest('output/templates/'));
    return stream; // 返回一个 stream 来表示它已经被完成

});

gulp.task('styles', ['clean'], function() {
    var stream = gulp.src(['src/styles/app.less'])
        // 执行一些代码检查,压缩,等
        .pipe(gulp.dest('output/css/app.css'));
    return stream;
});

gulp.task('build', ['templates', 'styles']);

// templates 和 styles 将会并行处理
// clean 将会保证在任一个任务开始之前完成
// clean 并不会被执行两次,尽管它被作为依赖调用了两次

gulp.task('default', ['build']);