Visual Studio 从 2013 版本开始引入了一个叫做“Browser Link”(浏览器链接)的功能,启用这项功能后,在 Visual Studio 中对 html、js、css文件进行修改后,只需要点击 Visual Studio 上的一个“刷新”按钮,所有打开了调试页面的浏览器窗口都会自动刷新,而不必和以往一样将界面切换回到各个浏览器按 F5。这个懒人功能在一定程度提高了开发效率(特别是在做多浏览器的兼容性测试时),但是一来,这个功能只能在 Visual Studio 上使用,二来仍不够方便,每次都要手动去点按刷新,那么是否能有一种更加通用、方便的方式来实现自动刷新即时预览?接下来介绍的就是一个可以实现这个功能的的 node.js 模块:livereload,只要五分钟,就能开启。
上一篇已经介绍了如何在项目中安装 Grunt。
npm install grunt --save-dev
grunt-contrib-watch
插件grunt-contrib-watch
是一个 Grunt 插件,它可以监视项目中文件的变化,然后执行特定的任务。比如,当插件监视到 less 文件被改动,可以自动地调用lessc
把文件编译为 css 文件。而 livereload 正是grunt-contrib-watch
内置的一个功能。
npm install grunt-contrib-watch --save-dev
往 Gruntfile.js 里添加代码:
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
watch: { // grunt-contrib-watch configuration:
livereload: { // target `livereload`
files: ['public/stylesheets/**/*.css', 'public/javascripts/**/*.js', 'views/**/*.jade'],
options: {
livereload: true // enable livereload
}
}
}
})
grunt.loadNpmTasks('grunt-contrib-watch')
}
注意:
files
属性是插件要监视的文件列表,在这个列表里的文件一旦发生了改变(修改、新增或者删除),都会触发对应的任务。例如,在上面的示例里,指示grunt-contrib-watch
插件如果检测到public/stylesheets
、public/javascripts
和views
文件夹(和子文件夹)中的.css
、.js
和.jade
文件有变,就会通知那些已经连接到livereload服务的网页刷新。options
中指定一行livereload: true
即可开启 livereload。watch
对象下的livereload
属性表示watch
任务的一个目标(target)(一个 Grunt 任务可以有多个目标,这样可以实现任务的复用,要运行指定的任务目标,只要执行grunt <任务>:<目标>
。)保存文件,然后运行:
grunt watch:livereload
接着在浏览器中访问 http://localhost:35729/,正常情况下,你会在浏览器上看到类似于下面的 JSON 响应:
{"tinylr":"Welcome","version":"0.2.1"}
最后一步。在你希望启用 livereload 的 HTML 页面内注入下面这段 JavaScript:
<script src="http://localhost:35729/livereload.js">script>
启动 web 服务器,然后在浏览器中访问网页。回到项目中,修改对应的源文件后保存,并切换回浏览器,你将会看到页面已经自动刷新为修改后的版本了。
livereload 通过引入的脚本livereload.js
在 livereload 服务和浏览器之间建立了一个 WebSocket 连接。每当监测到文件的变动,livereload 服务就会向浏览器发送一个信号,浏览器收到信号后就刷新页面,实现了实时刷新的效果。
如果你的应用启用了 HTTPS,尝试引入 livereload 脚本浏览器会给出一个警告并拒绝继续加载。这个警告产生的原因是浏览器不允许在通过 HTTPS 协议传输的页面中引入 HTTP 协议内容。当然你可以选择忽略这个警告并强制要求浏览器继续,也可以通过两行配置来为 livereload 启用 HTTPS 协议消除这个警告:把 Gruntfile.js
中的 livereload 配置从livereload: true
的布尔值修改为一个对象:
livereload: {
port: 4443,
pfx: require('fs').readFileSync('www.xxx.com.pfx'),
passphrase: ''
}
port
属性表示 livereload 服务的端口号,默认是35729。关键在pxf
和passphrase
两个属性,分别是网站 HTTPS 证书 pfx 文件和保护密码。(也可以用key
和cert
分别指定 PEM 格式的私钥和证书 key。)还可以传入额外的参数,这些参数可以传入到https.createServer
中。(参见 Node.js文档)
livereload 只应该用于开发环境。切记不要在生产环境里生成 livereload 脚本。因此在给页面写入 script 标签的时候,请严格区分运行环境。
不同的程序语言和框架区分运行环境(开发、测试、仿真、生产等)的方法有所差别,但一个非常简单的办法是通过一个服务启动时获取的标识符来区分:例如设定环境变量等。
下面是一段写在 ASP.NET Core 布局页里的代码,这段代码判断当前环境是Development
(服务器处理了
标签)时,才向客户端输出
标签内的内容。然后判断了配置中是否有 livereload 服务地址,如果有才产生标签注入 livereload 脚本。这个额外的判断在团队开发中可能有用处,你可以维护只属于自己的一套配置,而其他人员由于没有该配置,这样他们不使用 livereload 时就不会遇到 livereload.js 的脚本加载错误提示。另外,
标签中的 JavaScript 代码类似于加载 Google Analytics:它动态生成了
标签插入到页面里,同时设置了异步加载的
async
属性,可以避免 livereload 脚本影响页面其他内容的加载。
<environment names="Development">
@if (!string.IsNullOrEmpty(config["Diagnostics:Web:LiveReload"]))
{
<script>(function(g,c,e,d,f,b){if(d){f=c.createElement(e);f.async=1;f.src=d;b=c.getElementsByTagName(e)[0];b.parentNode.insertBefore(f,b)}})(window,document,"script",@Html.JavaScriptStringEncode(new Uri(new Uri(config["Diagnostics:Web:LiveReload"]), "livereload.js").ToString()))script>
}
environment>
一个实现类似功能的 Node.js Express 布局页(使用 Jade 模板引擎):
if settings.env === 'development' && settings.livereload
script.
(function(g,c,e,d,f,b){if(d){f=c.createElement(e);f.async=1;f.src=d;b=c.getElementsByTagName(e)[0];b.parentNode.insertBefore(f,b)}})(window,document,"script",!{JSON.stringify(settings.livereload)})
本篇文章中的示例代码在 GitHub 上可用。