优化博客开发环境和页面响应

之前博客使用的是gulp + gulp-webpack构建前端开发环境,存在无法支持热更新、无法自动同步文件hash等问题,因此在v0.5.0版本对整个静态资源开发环境进行了重构,比较完整地实现了node服务端渲染项目中的前端开发环境,下面是整个开发环境的实现整理。

<!--more-->

此次版本更新主要进行了下面工作

  • 优化开发环境和部署流程
  • 优化页面响应

1. 优化开发环境

在之前的版本,为了解决SEO的问题,进行了同构渲染,带来的一个问题是无法像SSR那样利用vue-cli比较舒适的前端开发环境,这个版本主要需要解决这个问题

理一下对于环境的需求

  • 在开发环境下,进行模块开发,支持scss、es6、热更新等基础功能
  • 在生产环境下,代码合并压缩、页面资源路径自动修改hash

乍一看直接使用webpack就可以解决这些需求,遇见的问题是:同构渲染的页面模板是后台渲染的,利用html-webpack-plugin无法及时为控制器提供需要渲染的模板,之前尝试输出后台模板,折腾过一次失败的HTML模块化尝试,后面发现这种通过webpack输出模板然后交给后端渲染不是一个很聪明的做法(除非是输出一个纯粹的html文件)

公司的项目一直在使用fis3进行打包,fis3有一个十分有用的特性:静态资源映射表,在这篇文章中,给出了基于资源映射表的模块化方案设计示例。

fis静态资源管理的核心是map表(文件中带RESOURCE_MAP标记的会自动替换成map表),里面记录了资源的请求地址、依赖等关键信息,可以根据项目需求添加更多的信息到其中。无论是哪种后端资源管理方案,都是通过读取map表来管理资源

1.1. 同步打包后的模板资源文件路径

根据这个思路,如果webpack也输出了这个静态资源映射表,就可以在后台项目中读取到,然后将对应的资源引用(script、link标签等)输出到模板中,完成webpack的输出文件和模板需要的静态资源之间的桥接,从而绕开html-webpack-plugin插件的引入。

因此,我们的问题就只有一个:在webpack打包时输出对应的map.json,为了实现这个功能,改写了npm run build脚本,相关代码位于/scripts/build.js下,核心逻辑如下

let config = require("../static/webpack.config")({production: true})
const bundler = webpack(config)

bundler.run((err, stats) => {
    let assets = stats.toJson().assets

    let outputFile = getOutputFileName(assets)
    outputFile = getOutputFilePath(outputFile, config.output.path)

    createOutputMap(outputFile)
})

每次后台项目每次启动时都会访问map.json,然后将资源路径埋入页面上

// render.js
let isProduction = process.env.NODE_ENV === 'production' // 是否为生产环境
let isDevelopment = process.env.NODE_ENV === 'development' // 是否为开发环境

// 获取生产环境的资源路径
function getProdResource() {
    try {
        return require("./map.json")
    } catch (e) {
        console.log("未检测到webpack输出文件,执行npm run build")
        throw e;
    }
}

// 获取本地开发环境webpack资源路径
function getDevResource() {
   // ..
}

let staticResource = isProduction ? getProdResource() : getDevResource()

module.exports = {
    staticResource,
    isProduction,
    isDevelopment
}

由于webpack输出的是一个完整的bundle文件,因此也不再需要requirejs和seajs等工具,因此在输出的map.json中,我们甚至不需要资源的依赖信息,只需要获取输出文件名即可。

根据map.json,我们可以确保在页面上访问到正确的资源,而重新打包编译的hash值会立即更新在map.json中,程序启动时重新获取相关输出,并映射到模板的静态资源标记即可。

1.2. 开发环境时热更新

在常规的webpack项目中,通过webpack-dev-server启动热更新服务器,在webpack.config.js中配置devServer,然后使用webpack-dev-server --inline启动即可

{
    entry: {
        index: path.resolve(__dirname, './index.js'),
    },
    devServer: {//配置此静态文件服务器,可以用来预览打包后项目
        contentBase: path.resolve(__dirname, './'),//开发服务运行时的文件根目录
        host: 'localhost',//主机地址
        port: 8080,//端口号
        compress: true, //开发服务器是否启动gzip等压缩
    }
}

此时,只需要在页面上引入localhost:8080/index.js路径,就可以实现开发环境热更新了,因此这个需求实现就比较简单,判断开发环境,然后开发环境输出webpack-dev-server服务器对应的虚拟资源即可

回到上面的render.js,补充getDevResource方法的实现

// render.js
function getDevResource() {
    let webpackConfig = require("../../static/webpack.config")({development: true})
    let {port} = webpackConfig.devServer
    return {
        css: `//localhost:${port}/index.css`,
        js: `//localhost:${port}/index.js`
    }
}

由于整个博客的页面数量较少,且采用同构渲染,因此只配置了一个入口文件;在多页面项目中,只需要稍微对map.json文件进行修改,即可按url分配对应模板所需的静态资源了。

1.3. 小结

通过webpack,解决了开发环境所需要的大部分需求,而map.json的解决了打包时输出和后台模板之间的同步问题。 此外此次版本更新升级webpack到了v4.26,部分配置与之前还是有一点差别。

下面是目前版本的一些指令,通过区分了开发环境和生成环境

开发环境

# 启动静态资源服务器
npm run static 
# 启动spervisor服务器
npm run dev

生产环境

# 静态资源打包
npm run build
# 启动服务器
npm run start

2. 优化页面响应

服务器访问速度一直提升不上去,切换页面也经常需要加载好几秒钟,此次进行了静态资源打包的升级,增加了文件hash值,因此可以配置nginx缓存。

考虑到博客更新频率不是很高,因此将域名下所有url都配置了一天的缓存。现在通过浏览器缓存访问博客,响应速度比较快,除了首次进入的页面,基本不会展示loading动画

server {
        listen 80;
        server_name www.shymean.com;

        root /usr/share/nginx/ShyMean/;
        index index.html index.htm;

        location / {
                proxy_pass http://127.0.0.1:3000;
                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

                expires 1d;
                access_log off;
                add_header Cache-Control "public";
        }
}

当然,关于站点的响应速度优化,仅仅配置nginx缓存是远远不够的,除开服务器硬件的限制,其他的优化方案也会陆续跟进,如

  • 静态资源部署至七牛云服务器
  • 字体图标体积优化
  • 样式、脚本文件懒加载

由于整个博客网站同构渲染的特殊性,与传统的服务端渲染及现在的单页面应用响应优化均有一些差异,因此后续优化方案会继续整理在博客中。

3. 其他

3.1. 增加demo页面

整理了一些练手项目,放在demo页面

3.2. 升级https

将博客站点升级到了https,相关记录:升级博客到HTTPS

3.3. 部署脚本优化

由于服务器性能比较差,npm installnpm run build时经常遇见问题,因此决定远程机器上仅做部署处理,不再执行打包功能。

function deploy() {
    let shell = require('shelljs');

    let version = shell.exec('lsof -i:3000', {silent: true}).stdout;

    let rePid = /\s(\d+)\s/.exec(version)
    let pid = rePid && rePid[1]

    let script = [
        "git pull origin master",
        // "npm run build",
        "forever stop 0",
        ( pid && `kill ${pid}`) || 'echo 1',
        `forever start -c "npm run start" ./`
    ]

    shell.exec(script.join(";"));
}

4. 小结

距离上次博客站点的更新(从SSR转换为同构渲染)已经过去了很长一段时间,这次的优化主要重构了前端的开发环境,为后续的性能优化和部署做一个铺垫。