前端模块化开发实验(一)——模块概念以及webpack基础配置
现在web 应用前后端分离,前端独立成一个专业,并且还工程化了,这个事实对于 经历过早期Web1.0和2.0的开发者而言,刚接触时应该是比较新奇的。
应用软件网络化,web services 成为不可阻挡的 软件应用的风潮。作为一名技术开发者,一名经历过早期web开发的人,我最感兴趣是 前端工程化的概念——什么东西被工程组件化了。
前端工程模块化的发展
有过早期经验有开发者应该知道,一支web应用 都是由多页组成的,页面在后端“渲染”生成;每个页面的交互功能都由自己js实现,当页面交互功能比较多时,都会让开发者在维护上的头痛。
当页面交互功能需求增长时,业界第一反应不是工程化技术,而是结构化和动态化技术。动态化是AJAX和Jquery的流行,结构化是第一代的MVC框架的出现,例如Backbone, Angular1。
前端MVC框架的出现应该是前端工程化的肇始,但它不成熟,尤其是第一代的。我个人为像webpack这样 module bundle工具出现才是 前端工程化 的真正的到来。为什么这么说,因为webpack 等 module bundle工具引入工程最需要“web模块”的概念。
工程化最主要技术基础,是分立制作的 工程构件,每种构件有自己的 独特的专业和特性。这话什么意思?意思是真正工程化是,我们可以独立制作不同功能的构件,然后根据需求像 拼积木那样 搭建想要的复杂功能。
web前端工程化最大的困难是,它 (web app)可怎么被分割为不同逻辑功能构件。有 webpack 使用经验的人都知道,不但js模块可以import,连css , 图片,字体都可以 import,这就是现在发展的 成果。但是在 webpack出现之前,只有 js 能被模块化,而且存在多种技术(commonjs, requirejs, AMD)。
前端模块化的基础原理
要对 web前端 工程化,第一 要件是清晰掌握 web app的本质形式,和它的可能逻辑构成;第二,是在第一个条件之上找 到一种技术实现,包括1)构件的表达,2)构件复合的机制,例如 webpack 处理 css 样式 就是一种工程化技术例子;而 MVC,MVVM是web app逻辑构成 比较流行的努力。
工程化成熟利好表现是,我们可以使用标准工程构件 分析、分解、设计和开发 我们的需求。在过去,web app 以页面为单位进行分析,设计和开发的,那是一种直观的未成熟的结构化开发。现在,有了webpack ,我们可以使用“web模块”进行工程化建构。
本系列文将以一个简单的例子,演示一下目前 前端工程化的样子——以webpack为例子,演示不同 web模块种类,及它们的复合原理,并且定制一个相对应的webpack配置。
我们选择一支简单但比较全面的web app 前端实现,演示可能的逻辑构件,和构件复合原理。
web 模块
在开始 1)分析需求和2)配置webpack之前,我们必须理解 何为 「标准工程构件模块」,以及有什么样的种类。
作为一种运行在web浏览器上的应用程序,主要有以下几种构件模块:
- 第一,与用户交互和与DOM有关联的 构件模块,可定名为 V 模块;
- 第二,样式模块,因为样式有自己的复杂性,可独立专业制作;
- 第三,包括 图片,字体等的 资源模块;资源可理解为 JS程序 特别的外部程序数据,通过url连接;
- 第四,还有一种 和以上三种都不同的 通用模块;
- 第五,React ,Vue 的模块,此模块 本文暂不处理;
- 第六,还有一个特别的模块,就是 index模块;
对这些模块类型需要补充几点:
- 第一,除了index模块,所有模块都是由 webpack loader处理,并“打包”入 bundle的;
- 第二,index模块 负责加载 bundle(通过scripte标签),包括外链的css ;
- 第三,这里的“模块”是 web 逻辑的,与 JS的语言程序模块不同,JS模块可理解为物理模块;
- 第四,样式和资源模块没有独立意义,它们都是为构建V 模块准备的;
一支 web app逻辑构成 标准是,它至少有一个index模块,一个或以上的V模块;V模块可选的由一个或以上的 样式和资源模块组成。
Welcome world应用
为了保证例子的基础 且全面 演示 一支 web 前端应用,我选择了一个 特别的Welcome world应用(相对于传统hello world)。功能非常简单,主页面有一个输入框,提示输入名字,点击后 显示欢迎语:XXX ,欢迎来到前端世界(Welcome to Web world)。
结构上,它由一个index 模块,两个 V模块(一个layout和一个主体内容),V模块会有一些样式,和图片。
为了支持这些 三种模块类 型的开发,我们必须配置webpack的 index插件,babel , css , asset loader。开始动手吧。
webpack 和 index模块
webpack 的工作 就是将 所有具有依赖关系的“web模块”打包成一个 bundle.js文件,然而 .js 自己并不会运行,在浏览器上要依赖一个页面执行它,这就是index.html。
index.html可直接在发布目录上手动创建,但是,更多理由表明,和bundle.js并行的 index 也应该要由 “源文件” 动态编译 ,和打包发布。「源文件」这个概念要特别解释一下,位于 src目录 的所有数据 都是 web模块 的「 源文件」,它不能直在浏览器上运行,必须进行“编译”和打包。
webpack 安装配置
$ mkdir modular-web-frontend-webpack-js && cd modular-web-frontend-webpack-js
$ npm init -y
$ npm i -D webpack webpack-cli
webpack.config.js
const path = require('path')
module.exports = {
entry: {
main: path.resolve(\_\_dirname, './src/index.js'),
},
output: {
path: path.resolve(\_\_dirname, './dist'),
filename: '\[name\].bundle.js',
},
}
package.json
"scripts": {
"build": "webpack --mode development"
}
index插件及和index模块
由于 index.html 在 bundle.js之外,故不由loader处理,由plugin处理(html-webpack-plugin)。
安装index 插件并配置webpack,创建src源文件目录,并在 src目录内创建 index 模块的”源文件“ —— index.template.html。
$ npm i -D html-webpack-plugin
webpack.config.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
/* ... */
plugins: [
new HtmlWebpackPlugin({
title: 'Hello Web world!!',
template: path.resolve(__dirname, './src/index.template.html'),
filename: 'index.html',
}),
],}
src/index.template.html
<!DOCTYPE html>
<html lang="en">
<head>
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<div id="root"></div>
</body>
</html>
bundle入口 index.js
index 模块 负责加载 bundle.js,所以 bundle.js可以 看成是 index模块 的子模块。而我们常常也将 bundle的入口模块也定名为index,这会引起困惑,不过也证明了它们之间有逻辑上的关系。这关系就是:
- index.html是由index插件生成的,boundle.js(index.js入口)由于loader生成的;
- index.template.html才对应index.js,同属于源文件模块;
- 与其他V有独立的DOM节点不同,index.js只负责访问index.html的根节点,并且只它(作为bundle一部分)访问
index.js主要任务,加载程序入口模块(下面的app.js),和挂接到index.html的根节点上:
import Greeting from './app.js'
// Append Greeting node to the DOM
const approot = document.querySelector('#root')
approot.append(Greeting)
程序入口 app.js和第一个V模块
这个模块是 整个程序的起点。此模块现在是访问DOM的,是一个简单的V模块,因为只是演示。实际项目中,这个模块会比较复杂,一般都不是V模块,而是为 更复杂V模块准备条件的特殊模块。
src/app.js
// Create Greeting node
const Greeting = document.createElement('h1');
Greeting.textContent = 'Hello Web World!'
export default Greeting;
webpack dev server
到此我们可以进行 编译、打包和测试了,打包好的数据会在发布目录dist上。为了完整,我们还要安装开发服务器(webpack-dev-server),不必每次修改都完整的打包、发布和测试。另外,还要安装清理插件(clean-webpack-plugin)。
npm i -D webpack-dev-server
webpack.config.js配置
const webpack = require('webpack')
module.exports = {
/* ... */
mode: 'development',
devServer: {
historyApiFallback: true,
static: path.resolve(__dirname, './dist'),
open: true,
compress: true,
hot: true,
port: 8080,
},
plugins: [
/* ... */
// Only update what has changed on hot reload
new webpack.HotModuleReplacementPlugin(),
],
})
package.json
"scripts": {
"start": "webpack serve"
}
Clean webpack plugin
npm install clean-webpack-plugin --save-dev
webpack.config .js
...
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
/* ... */
plugins: [
/* ... */
new CleanWebpackPlugin(), ],
}
Github源码
这里(https://github.com/nakeman/modular-web-frontend-webpack-js),在tag v0.1上。