今天请假肝学习 周三.
把一些重复的工作机械化和一些不被支持的新特转化,以及利用其它高效工具进行开发。
scss也是一种自动化构建,帮我们自动转换成原始css,但也要重复执行命令,安装 npm install -g sass
我们可以用npm script解决这些问题
package中新增一个对象
先安装下面这个,用于启动一个测试服务器
yarn add browser-sync --dev
package中添加
启动成功
我们要在这个服务器启动前设置样式,有个preserve之前的属性
添加变动监听变化 ,自动编译
因为加了watch命令行会一种占用着,我们需要启动多个任务去解决这个问题,先安装下面这个
yarn add npm-run-all --dev
build监听scss文件的变化去编译
browser-sync 监听文件变动去刷新浏览器
可以一起运行了
这样就可以刷改完后就不需要手动刷新浏览器获取最新的代码了(vue cli应该也是这个原理)
{
"name": "my-web-app",
"version": "0.1.0",
"main": "index.js",
"author": "zce (https://zce.me)" ,
"license": "MIT",
"scripts": {
"build": "sass scss/main.scss css/style.css --watch",
"serve": "browser-sync . --files \"css/*.css\"",
"start": "run-p build serve"
},
"devDependencies": {
"browser-sync": "^2.26.7",
"npm-run-all": "^4.1.5",
"sass": "^1.22.10"
}
}
没有webpack是因为 webpack严格说是模块打包工具。下面这些工具可以解决无聊重复性工作的自动化。
grunt老牌构建工具 生态完善 但基于临时文件构建的速度较慢(1读写磁盘多,2一个文件处理后再继续下一步)。
gulp很好解决了grunt的问题,因为是基于内存去实现的,相对磁盘读写肯定快了很多而且支持同时多任务,使用比grunt简单
fis是百度前端团队推出的构建系统,相对前俩个微内核的构建系统 fis更像是一个捆绑套餐,项目的大部分需求都已经集成了,资源请求性能优化等。
fis适合新手,其他适合灵活多变
新手希望规则,老手喜欢自由
了解 后面gulp为主
先npm init --yes 先init package.json
yarn add grunt
添加 gruntfile文件(入口文件)
多任务和默认任务
多任务
异步任务
可以看到异步的console.log不打印,grunt默认支持同步代码,这个时候需要 this。async 和 done()去解决,因为是异步任务,grunt会等done的执行才会结束这个异步任务
多任务要先配置好 init先 不然报错
配置好后
指定某目标 grunt build:css
插件是grunt核心
通配符的方式
子目录以及子目录下的文件
grunt-scss npm模块,使用不需要环境要求。 add grunt-sass sass --dev
报错解决 先指定实现的模块
可以添加这个获取soursemap文件
每次安装一个新模块都要 添加下面方法,很麻烦
解决方法
导入这个模块 后 就不需要重复导入插件了
添加babel目标配置 files配置输出和输入,而且也要设置选项,因为要转换es6(ECMA新特新转换)需要一个preset
添加配置选项和目标
题外话 scss和sass是同一个 因为某原因 scss变为了扩展名
一开始不会立即执行babel 只有在文件修改保存后执行
解决,做一个映射 这样启动的时候就会立即执行
const sass = require('sass')
const loadGruntTasks = require('load-grunt-tasks')
module.exports = grunt => {
grunt.initConfig({
sass: {
options: {
sourceMap: true,
implementation: sass
},
main: {
files: {
'dist/css/main.css': 'src/scss/main.scss'
}
}
},
babel: {
options: {
sourceMap: true,
presets: ['@babel/preset-env']
},
main: {
files: {
'dist/js/app.js': 'src/js/app.js'
}
}
},
watch: {
js: {
files: ['src/js/*.js'],
tasks: ['babel']
},
css: {
files: ['src/scss/*.scss'],
tasks: ['sass']
}
}
})
// grunt.loadNpmTasks('grunt-sass')
loadGruntTasks(grunt) // 自动加载所有的 grunt 插件中的任务
grunt.registerTask('default', ['sass', 'babel', 'watch'])
}
正题!
gulp高效易用。
1 新目录
2 yarn init
3 yarn add gulp --dev
安装gulp的同时会自动安装一个叫gulpcli的模块,这样就可以运行gulp命令
4 创建 gulpfile.js (导出函数的方式)
运行!yarn gulp foo 你导出的函数
报错了,解决:需要手动调用 done
gulp4.0前 注册方法需要一个模块去实现(了解即可,以后都是函数导出了)
const fs = require('fs')
exports.callback = done => {
console.log('callback task')
done()
}
exports.callback_error = done => {
console.log('callback task')
done(new Error('task failed'))
}
exports.promise = () => {
console.log('promise task')
return Promise.resolve()
}
exports.promise_error = () => {
console.log('promise task')
return Promise.reject(new Error('task failed'))
}
const timeout = time => {
return new Promise(resolve => {
setTimeout(resolve, time)
})
}
exports.async = async () => {
await timeout(1000)
console.log('async task')
}
exports.stream = () => {
const read = fs.createReadStream('yarn.lock')
const write = fs.createWriteStream('a.txt')
read.pipe(write) //pipe导入写入流中 文件流读完后有个end函数调用 这样gulp就知道结束了
return read
}
//文件流读完后有个end函数调用 这样gulp就知道结束了 以下代码模拟这个过程
// exports.stream = done => {
// const read = fs.createReadStream('yarn.lock')
// const write = fs.createWriteStream('a.txt')
// read.pipe(write) //pipe导入写入流中
// read.on('end', () => { //监听end时间
// done()
// })
// }
const fs = require('fs')
const { Transform } = require('stream')
exports.default = () => {
// 文件读取流
const readStream = fs.createReadStream('normalize.css')
// 文件写入流
const writeStream = fs.createWriteStream('normalize.min.css')
// 文件转换流
const transformStream = new Transform({
// 核心转换过程
transform: (chunk, encoding, callback) => {
const input = chunk.toString()
const output = input.replace(/\s+/g, '').replace(/\/\*.+?\*\//g, '') //转换空格 达到压缩的目的
callback(null, output)
}
})
return readStream
.pipe(transformStream) // 转换
.pipe(writeStream) // 写入
}
大多数都是用插件进行转换,相比原生的API 就是上面node哪个,gulp的api会更强大。
上面只是单纯输入输出,现在中间做加工,先下载压缩插件,yarn add gulp-clean-css --dev
yarn gulp后就获得压缩后的代码了。
再添加一个 指定名字插件 下载安装 导入 代码添加 运行gulp 获得处理后的重命名的min文件 yarn add gulp-rename --dev
下载代码
git clone https://github.com/zec/zec-gulp-demo.git
src文件夹下都会被转换,后面还可以配置压缩图片
开干!
先 yarn add gulp --dev
根目录创建 gulpfile.js
定义任务 (输出的文件夹dist dist意思是分发发布的意思)
写完导出才能使用,不然因为私有无法使用。
运行 gulp style
输出来的文件不是我们希望的 我们希望的是原来的目录结构 src/asset/style/…
解决:
添加转换过程(安装插件实现)
yarn add gulp-sass --dev(内部会安装node-sass)
导入
使用!
带_的不会被转换
新任务 添加script
yarn add gulp-babel --dev
导入
可能会报错,因为不像gulp-sass那样会自动帮你安装核心模块,babel需要你手动去安装
解决 yarn add @babel/core @babel/preset-env --dev
添加转换 ,运行!(注意一点要加 preset不然转换没效果,因为babel只是个平台不做转换,真正做转换的是内部的插件 比如preset)
const script = ()=>{
return src('src/assets/scripts/*.js',{base:'src'}).
pipe(babel({presets:['@babel/preset-env']})).
pipe(dest('dist'))
}
下载模板引擎转换插件 yarn add gulp-swig --dev
html模板中 使用的数据 这个时候就需要配置选择进行填充了
准备数据
const data = {
menus: [
{
name: 'Home',
icon: 'aperture',
link: 'index.html'
},
{
name: 'Features',
link: 'features.html'
},
{
name: 'About',
link: 'about.html'
},
{
name: 'Contact',
link: '#',
children: [
{
name: 'Twitter',
link: 'https://twitter.com/w_zce'
},
{
name: 'About',
link: 'https://weibo.com/zceme'
},
{
name: 'divider'
},
{
name: 'About',
link: 'https://github.com/zce'
}
]
}
],
pkg: require('./package.json'),
date: new Date()
}
有了数据后 在swing中传递
启动 填充成功!
这三个任务不能一个一个运行因为耗时没有必要 需要同时运行
创建编译任务 利用之前说的串行和并行任务
导入
此时就不需要导出三个任务 导出 parallel即可
yarn gulp compile
yarn add gulp-imagemin --dev
导入
添加代码
运行
压缩了多少都有显示
imagemin只会处理能压缩的文件(svg不能被压缩 需要其他插件 下面介绍)
字体SVG等也能压缩
yarn add del --dev
导入
在包装一下build执行顺序
导入 series
由于插件会越来越多不利于管理,比如每次你都要导入require一下很麻烦,可以安装 下面的插件解决
yarn add gulp-load-plugins --dev
导入
文件改动后自动更新页面
安装服务器(这个服务器可以监听文件,自动热更新)
yarn add browser-sync --dev
导入
把这个定义到任务中
3运行 yarn gulp serve
网页可能样式会乱
因为我们只处理我们自己的代码,如下代码没有处理
取消启动提示
设置port
是否自动打开
指定监听文件 **表示所有文件
导入 gulp api watch
这样就实现了源码修改后 同步到浏览器
试试 yarn gulp serve
(如果遇到不更新的问题可以用下面的方法)
开发阶段不需要监视文件压缩等,这样会加大开销
可以这样配置
先新增组合任务 因为可能有时候删了dist,这样serve就跑步起来了
开发阶段主要用compile就行了
yarn gulp develop试试效果
如果想图片字体改变了也重启的话
这些文件改变后,bs会reload,浏览器会重新请求这样就能拿到最新的了。
方法2 如果只想监控某一方法然后reload的话,这里reload传参是以文件流的形式
如果三个都加 那么 files就不需要了
走完上面基本就完成了,但是dist还是有点问题的
例如某些依赖其他文件夹的,并没有拷贝到dist目录,如果上线肯定找不到,本地可以是因为做了些配置路由映射
针对上述问题处理,1.low的方法就是直接拷贝过去,2.是下面要介绍的插件
被这些代码包括的会指定一个路径,也就是把包裹的文件打包到指定的文件目录,如果包裹多个会合并到一起,还会帮你自动压缩。
安装插件
yarn add gulp-useref --dev
(useref = 引用关系)
添加一个方法
注意要文件生成过后才有效,不然你找模板html是没用的
yarn gulp useref 运行!
新生成的就不和我们之前看到的一样了,把所有html里rel的css合并成一个文件了
多个js也被合并到一个了
这个过程中,可以多添加几个操作,下面会介绍到
上面说了 开干
安装压缩插件
yarn add gulp-htmlmin gulp-uglify gulp-clean-css --dev
需要判断对不同文件的操作,这个时候就需要一个额外的插件去判断,yarn add gulp-if --dev
记得先删除之前的文件不然没效果
如果没效果 看看你的dist index.html是不是没有那些包裹注释了
所以得先执行 compile 后执行 useref
记得上次才压缩 不然很慢
剩下的几个
yarn gulp compile
yarn gulp useref
这个时候会发现 main.css没内容,这是因为文件读写冲突,一边读一边写。
解决方法》
把最终的转换结果放在其他目录
这里发现html没压缩,html需要额外指定几个参数
yarn gulp useref 搞定!
还可以指定html一些空属性,注释删除。
因为之前的构建,现在上线前有多了一步useref,这么做就多余了,可以先放到temp目录,然后输出最终上线的dist
下面这个三个不需要放到temp,因为这三个只需要build的时候去做
,只有被useref影响的才需要
serve修改一个
useref 改俩个
先删除之前生成的文件
接下来要解决俩个小问题
一般leader构建完了这套系统 会发给小弟,但是你不想写文档和说明怎么去用的话,你就导出几个常用的就行了
clean compile build develop导出就行了。这样就简介很多。
第二步,在package中script中添加命令(因为会去node_module里找命令,这里就不需要run了)
小试牛刀
下面会介绍如何提取多个项目构建化过程 共同的自动化构建过程。
主要解决复用问题。这里主要讲解把刚刚做好的demo进行一个通用封装。
创建一个新仓库
创建新文件夹
下载脚手架
yarn global add zce-cli (我安装这个报错了 全局上找不到,到C:\Users\jack\AppData\Roaming\npm\node_modules 一看 确实没有)
npm i zce-cli -g 后 到C:\Users\jack\AppData\Roaming\npm\node_modules 有了
zce init nm zce-pages
创建好后进入目录
设置git
git add .
git commit -m’init’
刚刚做的gulpdemo 这里叫A 刚刚提交仓库的通用文件叫B
把A的依赖复制到B
在之前的demo中打开刚刚这个项目 code . -a -a表示同一窗口打开
1 把A的gulpfile 内容 复制到 B的index
2把A的devDependencies 复制到B的 Dependencies(因为这里的依赖一定会使用到)复制好后 在B yarn 一下 安装依赖
3 删除A项目的 node_module 3.1清空package中的devDependencies 和gulpfile
获得一个干净的项目
正常是先发布B 然后在A项目中使用,但现在是开发阶段,先link
把zec-page项目link到全局,这样其他项目也可以使用了 npm link 因为这里用的是npm link 所有build的时候要npm build yarn build会报找不到模块
回到A项目下 ,link后获得node_moudel的文件夹
因为zec-pages项目导出的是gulpfile,在A项目的gulpfile引用就好了,然后因为之前删除了生产依赖 先yarn一下
yarn build
报错
因为现在新项目下的node_module没有bin/gulp命令
安装
yarn add gulp-cli --dev
又报错,找不到本地gulp
yarn add gulp --dev
继续安装本地gulp
这些问题到后面发布后就不存在了,因为你下载B项目后会自动安装所有这些模块
继续报错
而且这里的Data是需要抽出来的,不可能每个项目都需要这样结构的data,我们可以在A项目中创建配置文件,然后读取
下面继续介绍怎么解决
应该把那些不应该提取的东西抽掉,例如data
先在新项目创建一个js文件
把data放到里面导出
在旧项目删除data,进行动态获取
把下面方法的data都换成configdata,这里不能config.data 只能 data: config.data 只有page方法要换 因为只有html模板用这些数据
回到新项目尝试运行
B项目的page中忘记添加bin了 bin的字段是整个项目的入口
这时又报另外的错误
新项目没有这个模块,解决》在旧项目对preset修改,require会自动去找一层一层地
现在正常了
对于代码里写死的路径例如下面的图,这样就不是很灵活了,现在我们进行自定义路径
配置
替换
完整代码
const { src, dest, parallel, series, watch } = require('gulp')
const del = require('del')
const browserSync = require('browser-sync')
const loadPlugins = require('gulp-load-plugins')
const plugins = loadPlugins()
const bs = browserSync.create()
const cwd = process.cwd()
let config = {
// default config
build: {
src: 'src',
dist: 'dist',
temp: 'temp',
public: 'public',
paths: {
styles: 'assets/styles/*.scss',
scripts: 'assets/scripts/*.js',
pages: '*.html',
images: 'assets/images/**',
fonts: 'assets/fonts/**'
}
}
}
try {
const loadConfig = require(`${cwd}/pages.config.js`)
config = Object.assign({}, config, loadConfig)
} catch (e) {}
const clean = () => {
return del([config.build.dist, config.build.temp])
}
const style = () => {
return src(config.build.paths.styles, { base: config.build.src, cwd: config.build.src })
.pipe(plugins.sass({ outputStyle: 'expanded' }))
.pipe(dest(config.build.temp))
.pipe(bs.reload({ stream: true }))
}
const script = () => {
return src(config.build.paths.scripts, { base: config.build.src, cwd: config.build.src })
.pipe(plugins.babel({ presets: [require('@babel/preset-env')] }))
.pipe(dest(config.build.temp))
.pipe(bs.reload({ stream: true }))
}
const page = () => {
return src(config.build.paths.pages, { base: config.build.src, cwd: config.build.src })
.pipe(plugins.swig({ data: config.data, defaults: { cache: false } }))
.pipe(dest(config.build.temp))
.pipe(bs.reload({ stream: true }))
}
const image = () => {
return src(config.build.paths.images, { base: config.build.src, cwd: config.build.src })
.pipe(plugins.imagemin())
.pipe(dest(config.build.dist))
}
const font = () => {
return src(config.build.paths.fonts, { base: config.build.src, cwd: config.build.src })
.pipe(plugins.imagemin())
.pipe(dest(config.build.dist))
}
const extra = () => {
return src('**', { base: config.build.public, cwd: config.build.public })
.pipe(dest(config.build.dist))
}
const serve = () => {
watch(config.build.paths.styles, { cwd: config.build.src }, style)
watch(config.build.paths.scripts, { cwd: config.build.src }, script)
watch(config.build.paths.pages, { cwd: config.build.src }, page)
// watch('src/assets/images/**', image)
// watch('src/assets/fonts/**', font)
// watch('public/**', extra)
watch([
config.build.paths.images,
config.build.paths.fonts
], { cwd: config.build.src }, bs.reload)
watch('**', { cwd: config.build.public }, bs.reload)
bs.init({
notify: false,
port: 2080,
// open: false,
// files: 'dist/**',
server: {
baseDir: [config.build.temp, config.build.dist, config.build.public],
routes: {
'/node_modules': 'node_modules'
}
}
})
}
const useref = () => {
return src(config.build.paths.pages, { base: config.build.temp, cwd: config.build.temp })
.pipe(plugins.useref({ searchPath: [config.build.temp, '.'] }))
// html js css
.pipe(plugins.if(/\.js$/, plugins.uglify()))
.pipe(plugins.if(/\.css$/, plugins.cleanCss()))
.pipe(plugins.if(/\.html$/, plugins.htmlmin({
collapseWhitespace: true,
minifyCSS: true,
minifyJS: true
})))
.pipe(dest(config.build.dist))
}
const compile = parallel(style, script, page)
// 上线之前执行的任务
const build = series(
clean,
parallel(
series(compile, useref),
image,
font,
extra
)
)
const develop = series(compile, serve)
module.exports = {
clean,
build,
develop
}
yarn build 试试
然后把这个配置复制到新项目中
抽象完毕
如何去掉gulpfile(这样每次就不用手动导出了,直接放在B项目里面使用就可以了,达到完全封装)
删除后 无法使用了 如图下
可以指定gulpfile,其实在这个新项目的node_module下这个index就是gulpfile
指定方法
但是这样也有问题,工作目录不是本目录了,需要指定一下
不过这样这么多参数就很复杂了
解决:集成cli
新建cli目录
2package中添加bin
继续配置入口文件
写完去新项目做个测试
先重新link一下,这样才能注册到全局
添加gulp命令
开始传递参数
执行成功
发布前先添加文件,file中的目录会发布到npm中
yarn publish 之前先提交git
yarn publish
如果不想改镜像源,添加一个参数即可
yarn publish --registry=https://registry.yarnpkg.com
publish到yarn的镜像源
新建目录测试
先zce-pages复制几个文件过来
运行!
可能会有同步问题,因为你发布的是npm源,你下载是从淘宝源,可能会存在同步问题,如果有下面有解决方式
登录淘宝镜像,搜你publish的包 然后更新
安装完后 node_module下有个bin目录,下面应该有这个
运行试试
添加script方便后续操作
了解即可