前言
之前花了篇文章讲述怎么从零开始搭建起一个基础的webpack4+React的脚手架,然后因为有些小项目不需要用到这么复杂的打包器去构建,这里分享一款项目中使用的简单配置的脚手架.
webpack系列文章
webpack4从零开始构建(一)
webpack4+React16项目构建(二)
webpack4功能配置划分细化(三)
webpack4引入Ant Design和Typescript(四)
webpack4代码去重,简化信息和构建优化(五)
webpack4配置Vue版脚手架(六)
FIS3 是什么?
FIS3 是面向前端的工程构建工具。解决前端工程中性能优化、资源加载(异步、同步、按需、预加载、依赖管理、合并、内嵌)、模块化开发、自动化工具、开发规范、代码部署等问题。
但是好像开发团队都散了,现在已经是弃置的样子,好几年没有消息,特别是Nodejs支持版本只到@6,所以如果对这方面有要求的话不建议使用,简单了解下即可.
因为这里摘抄了fis3构建流程和官网一些常用语法,更详细可以直接看官网,知道的就直接往下拉到配置部分
Node 版本管理
因为Fis3的Node版本要求 0.8.x,0.10.x, 0.12.x,4.x,6.x
,不在此列表中的版本不予支持
我们可以安装一个版本管理器,根据需求切换.
nvm-windows
安装版本
nvm install <version> [arch]
移除版本
nvm uninstall <version>
查看已安装版本
nvm ls
切换版本
nvm use [version] [arch]
我们选择系列最新的@6.14.4
版本安装
一些具体的包管理器等可以参考我之前的章节,这里不复述了.
安装 FIS3
npm install -g fis3
- -g 安装到全局目录,必须使用全局安装,当全局安装后才能在命令行(cmd或者终端)找到 fis3 命令
- 安装过程中遇到问题具体请参考 fis#565 解决
安装完成后执行 fis3 -v 判断是否安装成功
fis3 -v
命令行提供了以下功能
[INFO] Currently running fis3 (C:\Users\msi\AppData\Roaming\npm\node_modules\.fis3_npminstall\node_modules\.3.4.40@fis3\)
Usage: fis3 <command>
Commands:
init scaffold with specifed template.
install install components
release [media name] build and deploy your project
server <command> [options] launch a server
inspect [media name] inspect the result of fis.match
Options:
-h, --help print this help message
-v, --version print product version and exit
-r, --root <path> specify project root
-f, --file <filename> specify the file path of `fis-conf.js`
--no-color disable colored output
--verbose enable verbose mode
声明依赖
FIS3 在执行编译的过程中,会扫描这些编译标记,从而建立一张 静态资源关系表,资源关系表详细记录了项目内的静态资源id、发布后的线上路径、资源类型以及 依赖关系 和 资源打包 等信息。使用 FIS3 作为编译工具的项目,可以将这张表提交给后端或者前端框架去运行时,根据组件使用情况来 按需加载资源或者资源所在的包,从而提升前端页面运行性能。
工作原理
FIS3 是基于文件对象进行构建的,每个进入 FIS3 的文件都会实例化成一个 File 对象,整个构建过程都对这个对象进行操作完成构建任务。
整个 FIS3 的构建流程大体概括分为三个阶段。
1, 扫描项目目录拿到文件并初始化出一个文件对象列表
2, 对文件对象中每一个文件进行单文件编译
3, 获取用户设置的 package 插件,进行打包处理(包括合并图片)
单文件编译流程
lint:代码校验检查,比较特殊,所以需要 release 命令命令行添加 -l 参数
parser:预处理阶段,比如 less、sass、es6、react 前端模板等都在此处预编译处理
preprocessor:标准化前处理插件
standard:标准化插件,处理内置语法
postprocessor:标准化后处理插件
打包处理
prepackager 打包前处理插件扩展点
packager 打包插件扩展点,通过此插件收集文件依赖信息、合并信息产出静态资源映射表
spriter 图片合并扩展点,如 csssprites
postpackager 打包后处理插件扩展点
文件指纹
文件指纹,唯一标识一个文件。在开启强缓存的情况下,如果文件的 URL 不发生变化,无法刷新浏览器缓存。一般都需要通过一些手段来强刷缓存,一种方式是添加时间戳,每次上线更新文件,给这个资源文件的 URL 添加上时间戳。
而 FIS3 选择的是添加 MD5 戳
,直接修改文件的 URL,而不是在其后添加 query
。
<!--源码:
<img title="百度logo" src="images/logo.gif"/>
编译后-->
<img title="百度logo" src="/static/pic/logo_74e5229.gif"/>
资源定位
定位资源能力,可以有效地分离开发路径与部署路径之间的关系,工程师不再关心资源部署到线上之后去了哪里,变成了什么名字,这些都可以通过配置来指定。而工程师只需要使用相对路径来定位自己的开发资源即可。这样的好处是:资源可以发布到任何静态资源服务器的任何路径上而不用担心线上运行时找不到它们,而且代码具有很强的可移植性,甚至可以从一个产品线移植到另一个产品线而不用担心线上部署不一致的问题。
在html中定位资源
FIS3 支持对html中的script、link、style、video、audio、embed
等标签的src或href属性进行分析,一旦这些标签的资源定位属性可以命中已存在文件,则把命中文件的url路径替换到属性中,同时可保留原来url中的query查询信息。
相关配置
fis.match('*.{js,css,png,gif}', {
useHash: true // 开启 md5 戳
});
// 所有的 js
fis.match('**.js', {
//发布到/static/js/xxx目录下
release : '/static/js$0'
});
// 所有的 css
fis.match('**.css', {
//发布到/static/css/xxx目录下
release : '/static/css$0'
});
// 所有image目录下的.png,.gif文件
fis.match('/images/(*.{png,gif})', {
//发布到/static/pic/xxx目录下
release: '/static/pic/$1$2'
});
实际结果
<!--源码:
<img title="百度logo" src="images/logo.gif"/>
编译后-->
<img title="百度logo" src="/static/pic/logo_74e5229.gif"/>
<!--源码:
<link rel="stylesheet" type="text/css" href="demo.css">
编译后-->
<link rel="stylesheet" type="text/css" href="/static/css/demo_7defa41.css">
<!--源码:
<script type="text/javascript" src="demo.js"></script>
编译后-->
<script type="text/javascript" src="/static/js/demo_33c5143.js"></script>
在js中定位资源
js语言中,可以使用编译函数 __uri(path)
来定位资源,fis分析js文件或 html中的script标签内内容 时会替换该函数所指向文件的线上url路径。
<!--源码:
var img = __uri('images/logo.gif');
编译后-->
var img = '/images/logo_74e5229.gif';
<!--源码:
var css = __uri('demo.css');
编译后-->
var css = '/demo_7defa41.css';
<!--源码:
var js = __uri('demo.js');
编译后-->
var js = '/demo_33c5143.js';
在css中定位资源
<!--源码:
@import url('demo.css');
编译后-->
@import url('/demo_7defa41.css');
<!--源码:
.style {
background: url('images/body-bg.png');
}
编译后-->
.style {
background: url('/images/body-bg_1b8c3e0.png');
}
<!--源码:
.style {
_filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='images/body-bg.png');
}
编译后-->
.style {
_filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='/images/body-bg_1b8c3e0.png');
}
内容嵌入
嵌入资源即内容嵌入,可以为工程师提供诸如图片base64
嵌入到css、js里,前端模板编译到js文件中,将js、css、html拆分成几个文件最后合并到一起的能力。有了这项能力,可以有效的减少http请求数,提升工程的可维护性。
html
在html中可以嵌入其他文件内容或者base64编码值,可以在资源定位的基础上,给资源加 ?__inline
参数来标记资源嵌入需求。
html中嵌入图片base64
<img title="百度logo" src="images/logo.gif?__inline"/>
-----------------------------输出--------------------------------
<img title="百度logo" src="data:image/gif;base64,R0lGODlhDgGBALMAAGBn6eYxLvvy9PnKyfO...Jzna6853wjKc850nPeoYgAgA7"/>
html中嵌入样式文件
<link rel="stylesheet" type="text/css" href="demo.css?__inline">
-----------------------------输出--------------------------------
<style>img { border: 5px solid #ccc; }</style>
html中嵌入脚本资源
<script type="text/javascript" src="demo.js?__inline"></script>
-----------------------------输出--------------------------------
<script type="text/javascript">console.log('inline file');</script>
html中嵌入页面文件
<link rel="import" href="demo.html?__inline">
-----------------------------输出--------------------------------
<h1>demo.html content</h1>
在js中嵌入资源
在js中,使用编译函数 __inline()
来提供内容嵌入能力。可以利用这个函数嵌入图片的base64编码、嵌入其他js或者前端模板文件的编译内容, 这些处理对html中script标签里的内容同样有效。
在js中嵌入js文件
__inline('demo.js');
-----------------------------输出--------------------------------
console.log('demo.js content');
在js中嵌入图片base64
var img = __inline('images/logo.gif');
-----------------------------输出--------------------------------
var img = 'data:image/gif;base64,R0lGODlhDgGBALMAAGBn6eYxLvvy9PnKyfO...Jzna6853wjKc850nPeoYgAgA7';
在js中嵌入其他文本文件
var css = __inline('a.css');
-----------------------------输出--------------------------------
var css = "body \n{ color: red;\n}";
在css中嵌入资源
与html类似,凡是命中了资源定位能力的编译标记, 除了src="xxx"之外,都可以通过添加 ?__inline
编译标记都可以把文件内容嵌入进来。src="xxx"被用在ie浏览器支持的filter内,该属性不支持base64字符串,因此未做处理。
在css文件中嵌入其他css文件
@import url('demo.css?__inline');
-----------------------------输出--------------------------------
img { border: 5px solid #ccc; };
在css中嵌入图片的base64
.style {
background: url(images/logo.gif?__inline);
}
-----------------------------输出--------------------------------
.style {
background: url(data:image/gif;base64,R0lGODlhDgGBALMAAGBn6eYxLvvy9PnKyfO...Jzna6853wjKc850nPeoYgAgA7);
}
发布到远端机器
当我们开发项目后,需要发布到测试机(联调机),一般可以通过如 SMB、FTP 等上传代码。FIS3 默认支持使用 HTTP 上传代码,首先需要在测试机部署上传接收脚本(或者服务),这个脚本非常简单,现在给出了 php 的实现版本,可以把它放到测试机上某个 Web 服务根目录,并且配置一个 url 能访问到即可。
假定这个 URL 是:http://cq.01.p.p.baidu.com:8888/receiver.php
那么我们只需要在配置文件配置
fis.match('*', {
deploy: fis.plugin('http-push', {
receiver: 'http://cq.01.p.p.baidu.com:8888/receiver.php',
to: '/home/work/htdocs' // 注意这个是指的是测试机器的路径,而非本地机器
})
})
文件目录结构
从上面就知道FIS3 是基于文件对象进行构建的,首先我们要定义好文件目录
.
│───README.md // 说明
│───node_modules // 依赖
│───dist // 打包目录
│───.editorconfig // 格式化代码设置
│───.eslintrc.json // 代码风格设置
│───.gitignore // 提交文件过滤
│───package.json // 说明
│───fis-conf.js // fis3配置文件
│───dev.cmd // 执行开发命令
│───prod.cmd // 执行生产命令
└───assets
│ │───style // 公用scss和css
│ │───img // 公用图片
│ └───utils // 公用工具代码
│
└───static
│ │───plugins // 公用插件
│ │ └───文件夹 // 可能包含插件图片样式等
│ └───libs // 公用库
│
└───page // 页面部分
切记: 配置文件是具体针对项目文件夹路径打包,不能随意改动
打包目录结构
.
└───dist
│───img // 项目打包图片
│───index.html // 项目打包入口
└───pkg // 打包后的项目文件夹
│───all.css // 项目打包样式
│───libs.js // 项目打包库
└───page.js // 项目打包业务代码
安装依赖
时间关系直接附上,先安装完之后再讲解
package.json
{
"name": "fis3_demo",
"version": "1.0.0",
"scripts": {
"dev": "fis3 release dev -wL",
"prod": "fis3 release prod -c",
"lint": "eslint --fix",
"clean": "rimraf dist/"
},
"dependencies": {
"cross-env": "^5.2.0",
"eslint": "^5.9.0",
"fis-optimizer-clean-css": "^0.0.12",
"fis-optimizer-html-compress": "^0.0.7",
"fis-optimizer-png-compressor": "^0.2.0",
"fis-optimizer-uglify-js": "^0.2.3",
"fis-parser-babel-6.x": "^6.24.1",
"fis-parser-node-sass": "^1.0.4",
"fis3-optimizer-img-compressor": "^0.1.3",
"fis3-packager-deps-pack": "^0.1.2",
"fis3-packager-map": "^1.1.3",
"fis3-parser-typescript": "^1.2.2",
"fis3-postpackager-loader": "^2.1.11",
"fis3-preprocessor-autoprefixer": "^0.1.1",
"fis3-preprocessor-js-require-css": "^0.1.3",
"fis3-preprocessor-js-require-file": "^0.1.3"
},
"devDependencies": {
"fis-postpackager-map": "^0.0.2"
}
}
里面有两个命令分别执行开发环境和生产环境
"dev": "fis3 release dev -wL", // 监听文件实时更新
"prod": "fis3 release prod -c", // 清除缓存
参数回顾上面介绍命令行功能即可.为了方便使用不用每次打开终端,我们写两个执行文件
dev.cmd
npm run dev
prod.cmd
npm run prod
双击文件即可自动打开终端执行
配置文件
根目录新建fis-conf.js
,每次执行命令都会读取这里的配置去构建
忽略文件
因为fis3是根据文件打包,不像webpack4根据依赖解析,所以我们需要声明哪些文件不被加入构建队列中
fis.set('project.ignore', [
'/dist/**',
'/node_modules/**',
'/fis-conf.js',
'/package.json',
'/*.cmd',
'/doc/**',
'/yarn*',
'*.md',
'*.zip'
]);
样式处理
指向样式目录覆盖所有文件
fis.match('/assets/style/(**)', {
rExt: '.css',
// 支持 sass/scss 编译成 css
parser: fis.plugin('node-sass', {
// options...
}),
// 自动给 css 属性添加前缀,让标准的 css3 支持更多的浏览器.
preprocessor: fis.plugin('autoprefixer', {
browsers: ['Android >= 2.1', 'iOS >= 4', 'ie >= 8', 'firefox >= 15'],
cascade: true
})
})
- 支持sass/scss处理
- 指定浏览器版本自动添加前缀
脚本处理
fis.match('/assets/(js)/(**).js', {
// rExt: 'js',
// 支持 es6、es7 或者 jsx 编译成 es5
parser: fis.plugin('babel-6.x'),
/* 允许你在 js 中直接 require 文件。比如图片,json, 其他静态文件。
require 部分将会替换成部署后的 url。 同时还支持,如果文件小于 20K 直接替换成 base64 字符串。 */
preprocessor: [
fis.plugin('js-require-file'),
fis.plugin('js-require-css', {
mode: 'dependency'
})
]
})
- ES6兼容
- 支持js里require脚本和样式
页面&图片处理
fis.match('/assets/img/(**)', {
// 资源相对输出路径打包
release: './img/$1'
})
.match('/pages/(**)', {
release: './$1'
})
先不做处理,单纯输出指定位置
依赖处理
这个是打包的关键配置,可以直接使用打包相关依赖的文件,如果对打包顺序有要求的话需要自己控制.当然还有很多插件帮你更好处理.这是最基础的用法.当下以我的本地测试代码设置
fis.match('::package', {
postpackager: fis.plugin('loader'),
packager: fis.plugin('deps-pack', {
'pkg/libs.js': ['static/libs/jquery-1.11.0.min.js'],
'pkg/home/all.css': ['assets/style/reset.css', 'assets/style/style.scss'],
'pkg/home/page.js': [
'assets/js/index.js'
]
})
})
输出位置
首先需要指定打包路径,我们根据不同环境配置一下,到时候代码里所有相关路径都会被转成绝对路径.
const baseDir = './dist', // 项目打包路径
devDir = '../dist', // 开发资源路径
prodDir = '../dist'; // 线上资源路径
开发环境打包配置
// dev
fis.media('dev').match('*', {
domain: devDir,
deploy: fis.plugin('local-deliver', {
to: baseDir
})
});
意思就是将代码打包到baseDir
路径,里面的资源地址全部转成绝对路径devDir
+url
生产环境我们可以进行更多针对性操作
// prod
fis
.media('prod')
.match('*.{js,css,png}', {
useHash: true // 添加hash缓存
})
.match('/assets/js/(**)', {
// fis-optimizer-uglify-js 插件进行压缩,已内置
optimizer: fis.plugin('uglify-js')
})
.match('/pages/(**):js', {
// 针对html内js做压缩
optimizer: fis.plugin('uglify-js')
})
.match('/assets/style/(**)', {
// fis-optimizer-clean-css 插件进行压缩,已内置
optimizer: fis.plugin('clean-css')
})
.match('/pages/(**):{css,inline-style,scss}', {
// 针对html内css做压缩
optimizer: fis.plugin('clean-css')
})
.match('/assets/img/(**)', {
// 图片压缩
optimizer: fis.plugin('img-compressor')
})
.match('*', {
domain: prodDir,
deploy: fis.plugin('local-deliver', {
to: baseDir
})
});
基本配置到此就完成了,我们可以测试效果看看
用例
新建pages/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Page Title</title>
<link rel="stylesheet" type="text/css" media="screen" href="../assets/style/style.scss" />
</head>
<body>
<div class="container">
<img src="../assets/img/banner.jpg">
</div>
</div>
<!-- JS -->
<script src="../static/libs/jquery-1.11.0.min"></script>
<script src="../assets/js/index.js"></script>
</body>
</html>
然后根据里面对应的位置加上些资源执行命令即可,最终输出如下
.
└───dist
│───static // 源文件处理后的资源,已经被打包入pkg的libs.js中,可忽略
│───assets // 源文件处理后的资源,已经被打包入pkg的对应文件中,可忽略
│───img // 项目打包图片
│───index.html // 入口
└───pkg // 打包后的项目文件夹
│───all.css // 项目打包样式
│───libs.js // 项目打包库
└───page.js // 项目打包业务代码
最终我们得到的输出资源如上.至于被合并的资源应该有办法忽略不让输出,但是我没去找
还有当前项目没有引入模块插件,只是单纯讲解打包,有需要自行研究.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。