js模块化方案以及前端打包工具

by admin on 2020年1月5日

图片 1

图片 2

前端你懂的, 隔断时间不造个轮子会死人。

一、安装

  安装Webpack之前需要安装nodejs,然后用npm安装:

$ npm install webpack -g

 &nsbp;运行以上命令就将Webpack安装到了全局环境中。
  但是通常我们会将Webpack只安装到项目的依赖中:

$ cd /www/webpack_demo1      // 进入项目目录,确保该目录下存在有package.json文件,该文件之后会讲到
$ npm install webpack --save-dev  // 安装webpack依赖

打包工具可以更好的管理html,css,javascript,使用可以锦上添花,不使用也没关系。

图片来自知乎

为了解决什么问题

同样webpack一样,parcel官网首页的一句话基本就说清楚了为什么要用它

Blazing fast, zero configuration web application bundler

快速和零配置的打包工具基本说清了问题。我尝试了一下,因为是demo不知道实际开发纠结快多少,但还有一个让我觉得的亮点,就是在webpack开发的时候,有个变扭的地方就是入口文件是一个js,而以js为中心,打包好一切资源后,再插入到html文件中。而parcel却是可以以html为入口,对前端来说也应该更直观一点,因为前端最终是要以游览器为运行环境的。

至于0配置,我觉得有些过了,只加入了一些常用的前端打包需求,但离开箱即用有很大差距,比如现在前端几乎离不开的babel,还有postcss还是需要自己配置一下。要开发vue,react也需要相应的插件支持。

二、使用

  首先创建一个index.html和entry.js文件:

// index.html
<html>
<head>
  <meta charset="utf-8">
</head>
<body>
  <script src="bundle.js"></script>  // 注意这里是bundle.js不是entry.js
</body>
</html>

// entry.js
document.write('It works.')

  然后编译 entry.js 并打包到 bundle.js:

$ webpack entry.js bundle.js

  然后用浏览器打开会看到It works
  接下来添加一个模块module.js,并修改入口entry.js:

// module.js
module.exports = 'It works from module.js.'

// entry.js
document.write('It works.')
document.write(require('./module.js')) // 添加模块

  然后重新打包 webpack entry.js bundle.js 刷新页面可以看到变化
It works.It works from module.js.
  Webpack
会分析入口文件,解析包含依赖关系的各个文件。这些文件(模块)都打包到
bundle.js 。Webpack 会给每个模块分配一个唯一的 id 并通过这个 id
索引和访问模块。在页面启动时,会先执行 entry.js
中的代码,其它模块会在运行 require 的时候再执行。

  1. 前言1.1 前端

实践

官网上的例子我就不抄了,总之和最开始的前端开发一样,新建html,写script标签,而在js中可以任意使用import模块语法。
如果你碰到如下报错:

图片 3

error

那是因为你node版本不够,必须要node>=8以上。
升级版本即可。

下面编译一个vue的实例,用到我同事开发的一个parcel插件,parcel的插件不用配置,只需要安装在package.json里面,parcel会自己打包,所以安装好这个插件就可以开写了

npm i parcel-plugin-vue --save    

比较烦的一点是在vue的入口文件js里面要用到template,需要引用vue.esm.js,一般webpack是放在alias的,但parcel没有,很尴尬,所以app.js如下引用:

import Vue from 'vue/dist/vue.esm.js';

具体demo实例代码在github。这个工具还在不断探索中,如果遇到坑会不定期更新这个文章。

三、Loader

  Webpack本身只能处理js模块,如果要加载css\img…等静态资源就需要使用Loader转换
  Loader可以理解为模块和资源的加载器,它本身是一个函数,接受源文件为参数然后转换并返回。这样我们就可以通过require加载任何类型的模块或文件。
  Loader的特性:

  • 可以通过管道方式链式调用,每个 loader
    可以把资源转换成任意格式并传递给下一个 loader ,但是最后一个 loader
    必须返回 JavaScript。
  • Loader 可以同步或异步执行。
  • Loader 运行在 node.js 环境中,所以可以做任何可能的事情。
  • Loader 可以接受参数,以此来传递配置项给 loader。
  • Loader 可以通过文件扩展名(或正则表达式)绑定给不同类型的文件。
  • Loader 可以通过 npm 发布和安装。
  • 除了通过 package.json 的 main 指定,通常的模块也可以导出一个 loader
    来使用。
  • Loader 可以访问配置。
  • 插件可以让 loader 拥有更多特性。
  • Loader 可以分发出附加的任意文件。

  惯例loader一般是xxx-loader格式,eg:
css-loader。在引用loader的时候可以使用简写: json-loader可以写json。
  Loader可以在require()引用模块的时候添加,也可以在webpac全局配置中进行绑定,还可以通过命令行的方式使用。
  下面来说明下loader怎么用
  我们在页面中引入一个style.css文件,首页将style.css看成一个模块,使用css-loader读取它,再用style-loader把它插入到页面

/* style.css */
body { background: blue; }

  修改entry.js

require("!style-loader!css-loader!./style.css") // 载入 style.css
document.write('It works.')
document.write(require('./module.js'))

  安装loader:

$ npm install css-loader style-loader // 我在这里安装的时候貌似报了个错,后边加-g指定全局安装就好了

  重新编译打包可以看到页面背景颜色发生了变化
  如果每次require
CSS文件的时候都要写loader前缀,很麻烦,我们可以根据模块类型(扩展名)来自动绑定需求的loader。
  将entry.js中的require(“!style!css!./style.css”)
修改为require(“./style.css”),然后执行:

$ webpack entry.js bundle.js --module-bind 'css=style-loader!css-loader'

// 有些环境下可能需要使用双引号
$ webpack entry.js bundle.js --module-bing "css=style-loader!css-loader"

  这两种方式效果是一样的。

前端三剑客:结构层html,表现层css,行为层javascript。

四、配置文件

  Webpack处理在命令行中指定参数还可以通过制定配置文件来执行。默认情况下会搜索当前目录的webpack.config.js文件,这个文件是一个node.js模块,返回一个json格式的配置信息对象,或通过
–config 选项来指定配置文件。

  在项目中创建package.json(package.json是一个标准的npm说明文件,里面蕴含了丰富的信息,包括当前项目的依赖模块,自定义的脚本任务等等。在终端中使用npm
init命令可以自动创建这个package.json文件)来添加webpack需要的依赖:

{
  "name": "webpack-example",
  "version": "1.0.0",
  "description": "A simple webpack example.",
  "main": "bundle.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [
    "webpack"
  ],
  "author": "orlion",
  "license": "MIT",
  "devDependencies": {
    "css-loader": "^0.21.0",
    "style-loader": "^0.13.0",
    "webpack": "^1.12.2"
  }
}

  然后运行:

npm install

  然后创建一个配置文件webpack.config.js:

var webpack = require('webpack')

module.exports = {
  entry: './entry.js',
  output: {
    path: __dirname,
    filename: 'bundle.js'
  },
  module: {
    loaders: [
      {test: /\.css$/, loader: 'style-loader!css-loader'}
    ]
  }
}

  同时简化entry.js与style.css加载方式:

require('./style.css')

html好比是房子的地基,css和javascript是房子的建筑材料,这三个部分一起组成个漂亮的房子。我们不能把他们分开说,某某部分是个房子,只有三个一起才能组成一个漂亮的房子

五、插件

  插件可以完成loader不能完成的功能
  插件的使用一般是在webpack的配置信息plugins选项中指定。
  Webpack本身内置了一些常用的插件,下面我们利用BannerPlugin内置插件来演示一下。这个插件的作用是给输出的文件头部添加注释信息
  修改webpack.config.js,添加plugins:

var webpack = require('webpack')

module.exports = {
  entry: './entry.js',
  output: {
    path: __dirname,
    filename: 'bundle.js'
  },
  module: {
    loaders: [
      {test: /\.css$/, loader: 'style-loader!css-loader'}
    ]
  },
  plugins: [
    new webpack.BannerPlugin('This file is created by zhaoda')
  ]
}

  然后运行webpack,打开bundle.js。可以看到文件头部出现了我们指定的注释信息:

/*! This file is created by zhaoda */
/******/ (function(modules) { // webpackBootstrap
/******/  // The module cache
/******/  var installedModules = {};
...

1.2 JavaScript 的简介

这几年,javascript 发展非常快速,特别是在2015年,更是有一个质的飞跃。

1.2.1 ECMA

说到 JavaScript,就要说下Web标准的组织协会,ECMA,它是“European
Computer Manufactures
Association”的缩写,中文称欧洲计算机制造联合会,1961年成立,旨在建立统一的电脑操作格式标准–包括程序语言和输入输出的组织。

1.2.2 JavaScript

2015年,JavaScript
引入许多新的语法糖,而且制定过程当中,还有很多组织和个人不断提交新功能。事情很快就变得清楚了,不可能在一个版本里面包括所有将要引入的功能。

常规的做法是先发布 6.0 版,过一段时间再发 6.1 版,然后是 6.2 版、6.3
版等等 ,这个2015年之前 JavaScript
现在习惯称为ECMAScript5,而之后称为ECMAScript6。

标准委员会商定后最终决定,标准在每年的 6
月份正式发布一次,作为当年的正式版本。接下来的时间,就在这个版本的基础上做改动,直到下一年的
6
月份,草案就自然变成了新一年的版本。这样一来,就不需要以前的版本号了,只要用年份标记就可以了。

因此,ES6 既是一个历史名词,也是一个泛指,含义是 5.1 版以后的 JavaScript
的下一代标准,涵盖了 ES2015、ES2016、ES2017 等等,而 ES2015
则是正式名称,特指该年发布的正式版本的语言标准。本书中提到 ES6
的地方,一般是指 ES2015 标准,但有时也是泛指“下一代 JavaScript 语言”。

问题一:关于ECMAScript 和 JavaScript 是什么关系 ?

回答:从现在的角度来看,二者是可以互换的。即ECMAScript是JavaScript
,JavaScript 是ECMAScript。

问题二:ECMAScript 6 和 ECMAScript 2015 是什么关系 ?

回答:ECMAScript 6泛指下一代 JavaScript 语言,ECMAScript 2015指的是
2015年的 JavaScript 标准;

总结

// es6 泛指下一代 JavaScript 语言,当时部分人也会认为特指ES2015ECMAScript6.0 = ECMAScript2015 = es2015 = es6(部分人会这么认为)ECMAScript6.1 = ECMAScript2016 = es2016 = es7(部分人会这么认为)ECMAScript6.2 = ECMAScript2017 = es2017 = es8(部分人会这么认为)

1.2.3 浏览器的遭遇

很尴尬的是,JavaScript发展很快,但是浏览器跟不上脚本更新的进度。一方面给出了标准,一方面却不能直接在浏览器上使用。

这就出现了Babel,Babel自称是 JavaScript
编译器,它的作用就是将ES6新语法转成ES5,即现在浏览器可识别的脚本。

但是使用 Babel
编译也有缺陷,那就是每一次保存,都需要手动的使用命令行编译,而且编译过程中还需要相关联的包配合使用,很繁琐。所以,打包工具就出现了,它可以帮助做这些繁琐的工作。

  1. 打包工具2.1 介绍

仅介绍 4
款主流的打包工具:grunt,gulp,webpack,rollup,以发布时间为顺序。

Grunt:最老牌的打包工具,它运用配置的思想来写打包脚本,一切皆配置,所以会出现比较多的配置项,诸如option,src,dest等等。而且不同的插件可能会有自己扩展字段,认知成本高,运用的时候需要明白各种插件的配置规则。Gulp:用代码方式来写打包脚本,并且代码采用流式的写法,只抽象出了gulp.src,gulp.pipe,gulp.dest,gulp.watch接口,运用相当简单。更易于学习和使用,使用gulp的代码量能比grunt少一半左右。Webpack:
是模块化管理工具和打包工具。通过 loader
的转换,任何形式的资源都可以视作模块,比如 CommonJs 模块、AMD 模块、ES6
模块、CSS、图片等。它可以将许多松散的模块按照依赖和规则打包成符合生产环境部署的前端资源。还可以将按需加载的模块进行代码分隔,等到实际需要的时候再异步加载。它定位是模块打包器,而
Gulp/Grunt 属于构建工具。Webpack 可以代替 Gulp/Grunt
的一些功能,但不是一个职能的工具,可以配合使用。Rollup:下一代 ES6
模块化工具,最大的亮点是利用 ES6
模块设计,利用tree-shaking生成更简洁、更简单的代码。一般而言,对于应用使用
Webpack,对于类库使用 Rollup;需要代码拆分(Code
Splitting),或者很多静态资源需要处理,再或者构建的项目需要引入很多
CommonJS 模块的依赖时,使用 webpack。代码库是基于 ES6
模块,而且希望代码能够被其他人直接使用,使用 Rollup。2.2 使用总结

Grunt:MPA,老牌打包工具,基于文件为媒介

2.3 如何选择

如果你一个都不熟悉的话,那么我直接推荐webpack,官方文档非常详细,更新频率很高。而且在其他的打包工具在处理非网页文件基本还是需要借助它来实现。最关键现在的脚手架主流依旧是它。

如果在处理文件需要关注前端三剑客的话,那么grunt和gulp会更好点,这两者我直接推荐gulp,除非你已经很熟悉grunt了。

如果你更加在意脚本代码的简洁精炼,那么可以使用rollup

如果你还要更加精炼一点,这里新出来一个新的打包工具,免插件式parcel

  1. 个人打包配置

在打包上,我个人注重的是配置从简单到复杂,所以我分开使用。

css打包选择了gulp,2个任务,3个插件,有一个插件是为了编译scss,如果直接使用css,那么这个插件也可以去除。

// 任务一:编译gulp.task('compile', function () { return gulp.src('src/scss/*.scss') .pipe(sass({outputStyle: 'expanded'})) // 插件一:编译scss .on('error', showError) .pipe(autoprefixer({ // 插件二:自动添加浏览器前缀 browsers: [' 1%', 'last 4 versions'], cascade: false, remove: true })) .pipe(cleanCss({ // 插件三:压缩样式 compatibility: 'ie8', format: 'keep-breaks' })) .pipe(gulp.dest('../dist/css'));})// 任务二:观察gulp.task('watch', function(){ gulp.watch('src/scss/*.scss', ['compile'])})

ECMAScript个人现在基本使用es6,所以在打包脚本上我选择了rollup,只提取有用的代码,配置上参考react官方配置文档

import resolve from 'rollup-plugin-node-resolve';import babel from 'rollup-plugin-babel';import commonjs from 'rollup-plugin-commonjs';import { eslint } from 'rollup-plugin-eslint';import { uglify } from 'rollup-plugin-uglify';const env = process.env.NODE_ENV;console.log('当前环境:%s', env);const configs = [ { input: 'src/js/index.js', output: { file: 'dist/js/index.js', format: 'umd', name: 'atom', banner, sourcemap: true } }]const plugins = [ eslint({ // 检测js代码语法格式 formatter: 'codeframe', include: [ 'src/js/**/*.js' ] }), resolve({ // 提取所依赖的代码 jsnext: true, main: true, browser: true, module: true }), babel({ // 编译es6 - es5 exclude: 'node_modules/**' // 只编译我们的源代码 }), commonjs() // 将commonjs 转成 es6 ]export default configs.map(v = { v.plugins = plugins if (env === 'development') { v.watch = { // 监听脚本的变化 include: 'src/js/**', exclude: ['node_modules/**'] } } if (env === 'production') { v.plugins.push( uglify({ // 压缩脚本 compress: { pure_getters: true, unsafe: true, unsafe_comps: true, warnings: false } }) ) } return v});

html个人不做任何处理,可以在上线压缩减少文件的体积,压缩直接使用gulp

==> removeEmptyAttributes: true,//删除所有空格作属性值 ==>
removeScriptTypeAttributes: true,//删除

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图