2020年4月1日,毕业将近两年的我完成了第一次职场跳槽,入职到现在所在的公司,上家是一家电商公司,所做项目就是一个供公司内部使用的ERP系统。作为一个小小的初级前端搬砖工人,我主要负责的工作就是根据高级工程师搭建好的大框架,在里面重复使用大佬写好的轮子实现页面,基本上没有机会去基础底层的框架使用。新公司是一家大数据业务公司,前端需要用到的技术会多很多,前后端都需要用到。持续学习,不断进步,共勉。
来到新公司所接到的第一项工作是对公司官网页面的重构,大体框架还是以前的大佬实现好的,用到的技术栈页确定了,但是对我来说都是新知识。最后用了两个星期左右的时间才终于做好并初步交付成功,藉此机会,记录一下在这个项目学习和使用到的新知识。
官网地址:sugo.io/
主要技术
- 打包工具:Webpack
- 服务器:koa
- 前端:Ejs、Sass、Bootstrap、jQuery、jQuery.tmpl
Webpack
我对 webpack 的了解只属于入门级别,只是对照入门教程简单使用了一下。
下面记录一下使用到的 plugin 。
plugins: [
new ExtractTextPlugin({
filename: 'css/[name].css',
allChunks: true
}),
new webpack.optimize.UglifyJsPlugin(),
new webpack.LoaderOptionsPlugin({
postcss: [autoprefixer]
})
]
1. extract-text-webpack-plugin
这个插件可以将匹配到的 .css / .sass 文件分别打包到分离的 css 文件,这样就不会内联到 JS Bundle 。JS Bundle 和 CSS Bundle 是平行加载的,如果你的 css 文件很大,这样可以达到提速的效果。因为这个官网是单页面应用,每一个页面对应一个路径,因此使用这个插件的配置后可以把对应的 .sass 打包到 对应文件名的 .css 文件。filename: 'css/[name].css'
学习文章:extract-text-webpack-plugin 、webpack学习笔记之六,extract-text-webpack-plugin
2. UglifyJsPlugin
uglifyJsPlugin 是用来对 JS 文件进行压缩的,压缩 JS 文件的大小,以加快 load 速度。但是有个问题就是这个插件会拖慢 webpack 的编译速度,因此建议在开发的时候不要用它,在部署到生产环境的时候再打开。具体方法就是在 webpack.config.js 文件里面加个是否是生产环境的判断就行了。
if (process.env.NODE_ENV === 'production') {
exportObj.plugins.push(new webpack.optimize.UglifyJsPlugin())}
3. LoaderOptionsPlugin
用来加载 loader 的一些配置选项。比如一个网站需要适配不同的浏览器,css 样式上需要加不同的前端来区别。但是我们不可能每次都手动去写,这样麻烦而且增加了工作量。这时候就可以使用 postcss loader 来解决,然后在 LoaderOptionsPlugin 这个插件这里进行 postcss loader 的配置。具体步骤可以参考:Webpack使用less以及postcss-loader
Koa
与 Express 一样,Koa 也是基于 node.js 的 web 开发框架。使用 async 函数丢弃函数回调,增强错误处理能力,相对于其它开发框架来说,Koa 更简洁,不捆绑任何中间件,而是提供一套功能强大的方法。具体参考官方:Koa 官方文档 。下面是这个官网项目服务器端需要用到的一些中间件。
1. koa-mount
将其它应用程序作为中间件挂载到当前 koa ,传递给 mount 函数的路径会暂时从 URL 中剥离出来,直到堆栈释放。
const Koa = require('koa')
const mount = require('koa-amount')
// a
const a = new Koa()
a.use(async (ctx, next) => {
await next()
ctx.body = 'Hello'
})
// b
const b = new Koa()
b.use(async (ctx, next) => {
await next()
ctx.body = 'World'
})
// app
const app = new Koa()
app.use(mount('/hello', a))
app.use(mount('/world', b))
app.listen(3000)
学习链接:koa 帮助文档
2. koa-router
koa-router 是用于实现路由功能的一个中间件,一个比较有趣的说法就是,koa 就像一个菜市场,它本身只需要维护菜市场的正常运行即可,而一系列额外的中间件就像菜市场中的菜摊,与我们打交道的大多都是这些中间件菜摊,而 koa-router 就是其中生意巨好的一个。
const Koa = require('koa')
const app = new Koa()
const router = require('koa-router')()
router.get('/', async ctx => {
ctx.body = 'hi there'
})
app.use(router.routes())
.use(router.allowMethods())
学习链接:
koa-router是什么?详解koa-router的大致结构与运行流程
3. koa-static
koa-static 用来在服务器配置静态资源目录,方便读取文件。
const send = require('koa-static')
const staticOption = () => ({
maxAge: 1000 * 60 * 60 * 24 * 365,
hidden: true, gzip: true
})
app.use(send(path.resolve(__dirname, './public'), staticOption()))
比如我们在工程的 public/img 中保存项目需要用到的图片资源,在代码中配置好静态资源路径后 app.use(send(path.resolve(__dirname, './public'))我们就可以在项目中很方便地读取到静态资源文件夹下的文件了,注意,在输入读取路径时不需要再输入 public ,直接 img/xxx.png 就可以了。
4. koa-ejs
const render = require('koa-ejs')
const path = require('path')
const Koa = require('koa')
const app = new Koa()
render(app, {
root: path.resolve(__dirname, './view'),
layout: false,
viewExt: 'ejs',
cache: true,
debug: process.env.NODE_EVN === "development"
})
用于 ejs 模板的使用,具体参数可参考:官方文档
5. koa-etag 和 koa-conditional-get
这两个中间件互相搭配使用,实现基于 etag 的缓存功能。etag 判断内容是否发生改变,若内容未发生改变,返回304。否则返回带有内容的回复。
const etag = require('koa-etag')const conditional = require('koa-conditional-get')
app.use(conditional())app.use(etag())
学习链接:
6. koa-bodyparser
koa-bodyparser 是可以把 post 请求的请求参数解析到 ctx.request.body 中的中间件。
const parser = require('koa-bodyparser')
app.use(parser())
Ejs
ejs 就是一个 JavaScript 的嵌入式模板,其中的 ‘e’ 就是 effective (效率)的意思,通过特定的操作符可以用数据动态生成 html ,用起来还是比较有趣和方便的。具体的使用在官方文档中写得非常详细,这里就不做赘述了。说一下我在使用期间遇到的一个小难点,我在服务器端定义好了静态数据,通过全局变量传递到 ejs 模板页面后,使用 <% - datas.caseDatas %> (datas) 就是我定义的对象,但是在 ejs 模板页面中,我想写一点 JavaScript 脚本处理一些交互,突然发现不知道怎么去拿服务器端传递下来的数据了,后面经过尝试发现,在
const caseDatas = JSON.parse('<%- JSON.stringify(datas.caseDatas) %>');
Sass
Sass 是非常强大的 css 拓展语言,以前我只听说过学习过基础,但是没有在实际项目中使用过。通过这个项目的锻炼,我总结了 sass 中几个比较常用的功能。
先贴一下官方文档:www.sass.hk/docs/
1. 嵌套规则
sass 可以将一套 css 样式嵌套到另一套 css 样式中,里层的选择器会将外层的选择器作为父选择器,使用 & 符号可以得到父选择器。
@function grid-width($container, $num, $gutter) {
@return floor(($container - $gutter * ($num - 1)) / $num);
}
.right {
$gutter: 10px;
$img-width: grid-width(570px, 3, $gutter) - 10;
$img-height-row: $img-width * 9 / 14;
$img-height-col: $img-width * 16 / 12;
width: 50%;
height: 500px;
overflow-y: scroll;
&:hover {
@extend %box-shadow;
}
2. 常量定义与四则运算
如上面贴的代码所示,sass 可以在选择器定义局部常量,甚至可以在 sass 文件开头定义全局常量。还可以使用+-*/实现四则运算,还可以将常用的计算提取成函数,通过参数传递需要计算的值,返回计算结果。然后直接通过函数名调用,跟写 JS 一毛一样哈哈。
3. 混合指令
混合指令(Mixin)用于定义可重复使用的样式,避免了使用无语意的 class,比如 .float-left。混合指令可以包含所有的 CSS 规则,绝大部分 Sass 规则,甚至通过参数功能引入变量,输出多样化的样式。
@mixin silly-links {
a {
color: blue;
background-color: red;
}
}
@include silly-links;
sass 有很多有趣的功能,很高效并且使用起来非常简单。一般看两篇官方文档就可以慢慢去写了,边用边学才能印象深刻。
jQuery.tmpl
jquery.tmpl 跟 ejs 一样,都是一个 JS 嵌入式模板,但是为什么我既然用了 ejs ,又要使用 jQuery.tmpl 呢?在实现案例页面的时候,需求需要做到分页后动态刷新页面,而不是整个页面重新加载,使用 ejs ,我只能通过 url 传参重新加载页面后才能拿到分页的参数进行数据过滤,实现分页。但这不是我想要的效果,后面我发现 jQuery.tmpl 这个模板更加方便,它可以动态修改 DOM 结构,通过 JSON 的格式传递和绑定数据,体积小速度快。
常用的标签就只有几个:${}、{{each}}、{{if}}、{{else}}
<script id="paginationTmpl" type="text/x-jquery-tmpl">
{{each(i, it) arr}}
<a href="javascript:;" class="${$data.pageNum==it ? "active" : ''}">${it}</a>
{{/each}}
</script>
// data:拼凑好的需要在模板里面使用到的数据
$('#paginationTmpl').tmpl(data).appendTo('#paginationDiv');
pm2
pm2是一个进程管理工具,可以用它来管理你的node进程,并查看node进程的状态,当然也支持性能监控,进程守护,负载均衡等功能。
在项目开发接近尾声的时候,我们可能需要把开发成果展示给测试或者 UI 看,但是又不想每次都去部署测试环境,太麻烦了。于是我用 pm2 发布工程到本地 IP ,让其他人访问我的 IP 。在验收阶段,UI 提出一些优化,我这边快速解决并且重新打包发布一下就可以让 UI 快速看到结果了,这大大提高了效率。
全局安装:npm i -g pm2
启动进程:NODE_ENV=production pm2 start app.js
重新打包:npm run build
重新发布:pm2 restart all
总结
上面只是对这次项目中使用的只是做一个比较粗略地概况,希望工作之余可以抽时间对这些知识进行深入的研究,理解底层。