从项目搭建之初,我们前端开发人员使用vue,react,angular等这些框架搭建的时候,随着项目不断的迭代开发,项目的体积与存量也在不断的变大,导致我们这些基于 webpack 构建的大型项目开发速度已经非常慢了,而我们前端开发人员,也慢慢的对于项目的改动编译已经逐渐习惯忍受超过几分钟的项目start启动时间,HRM 热更新超过1分钟的 reload 时间。即便我们不停的去使用一系列手段去优化,或者使用被寄予厚望的最新版本的 webpack5 内置了缓存机制也不会得到质的提升。
我们使用webpack这类工具,主要还是它基于CJS,可以实现模块化管理与应用处理。而目前的浏览器也支持了 ESM import 模块化加载方案,终于原生浏览器就可以轻易支持了文件模块化,这使得本地构建不再需要依赖webpack 去做处理模块化关系并聚合文件。
如果大家对于ESM不是很熟悉的,可以尝试阅读这篇文章图说 ES Modules
ES Modules 是用于处理模块的 ECMAScript 标准。 虽然 Node.js 长期使用 CommonJS 标准,但浏览器从未有过模块系统。 每个主要决策(如模块系统)必须首先由 ECMAScript 标准化,然后由浏览器实施。
这个标准化过程在 ES6 中完成,浏览器开始实施这个标准,试图以相同的工作方式保持一致性,,现在 Chrome,Safari,Edge 和 Firefox(从 60 版本开始)支持ES 模块。
ESM是ES6的一部分,我们开发过程中,也已经使用了,但是 ESM 已经浏览器中可用,但是很大程度上依然要依靠 webpack, babel等第三方插件来编码转换,但是直接用语言构建这样的功能对于统一模块在客户端和 Node.js 上的工作方式来说是巨大的挑战。
但是
ESM 带来的最直观的改变有下面三点:
这些特性是的我们的项目启动与HRM热更新非常快,等待的时间几乎可以忽略,而这些是其他工具所不具备的。
snowpack是一种现代的,轻量级的构建工具,可以加快Web页面开发的速度。每次保存、修改单个文件时,传统的JavaScript构建工具(例如webpack和Parcel)都需要重新构建和重新编译打包应用程序的全面依赖流程。重新打包步骤发生在保存更改和看到更改反映在浏览器之间的时间间隔。
snowpack在开发过程中为您的应用程序提供无编译打包式服务。每个文件只需要构建一次,然后就可以永久缓存。文件更改时,Snowpack会重建该单个文件。没有任何时间浪费在重新打包每个更改上,只需在浏览器中进行即时更新(通过热模块替换(HMR)更快地进行更新)。
Snowpack的无编译打包开发仍支持您用于生产的编译打包版本。当您构建用于生产的应用程序时,可以通过Webpack或Rollup 的官方Snowpack插件插入您喜欢的编译打包程序。Snowpack已经可以处理您的构建,因此不需要复杂的编译打包程序配置。
Snowpack 提供了两全其美的优势:快速,无编译打包的开发,在编译打包的生产版本中具有优化的性能。
snowpack 原理如图:
无编译打包开发是在开发过程中将单个文件传送到浏览器的想法。仍然可以使用您喜欢的工具(例如Babel,TypeScript,Sass)来构建文件,然后借助ESM import和export语法将它们与依赖项分别加载到浏览器中。每当您更改文件时,Snowpack都会仅重建该文件。
替代方法是编译打包开发。如今,几乎所有流行的JavaScript构建工具都将重点放在编译打包开发上。通过编译打包程序运行整个应用程序会给开发工作流程带来额外的工作和复杂性,由于ESM得到了广泛的支持,因此这是不必要的。必须将所有更改(每次保存)与应用程序的其余部分重新编译打包在一起,然后更改才能在浏览器中反映出来。
与传统的编译打包开发方法相比,无编译打包开发具有多个优点:
最后一点很关键:每个文件都是单独构建的,并无限期地缓存。您的开发环境将永远不会创建文件超过一次,浏览器也不会下载文件两次(直到更改)。这是无编译打包开发的真正力量。
# npm:
npm install -g snowpack
# yarn:
yarn add -g snowpack
#pnpm:
pnpm add -g snowpack
首先,为Snowpack项目创建一个空目录。也可以直接使用Snowpack cli 命令行来初始化
npx snowpack [command]
yarn run snowpack [command]
pnpm run snowpack [command]
相关的cli command , 大家查看官网吧~
项目搭建我们还是按照步骤来吧~ 毕竟后续我们的代码维护都是需要我们自己的维护的,后续的代码结构也肯定跟官方给出的模板会存在差异的,很多都是我们根据实际开发过程中逐步自定义化的。
mkdir my-first-snowpack
cd my-first-snowpack
Snowpack是从npm安装的软件包。package.json在项目目录中创建一个文件来管理依赖项。在您的项目中运行以下命令以创建一个简单的空package.json:
npm init
npm install --save-dev snowpack
snowpack 这个库是用来创建项目的,如果不想一直让他存在电脑里面,可以使用 npx 来创建 (比较推荐使用这种方式去创建项目),当然如果大家不想用官方的create-snowpack-app来创建,也可以就单独安装 snowpack 然后配置snowpack.config.js 来实现项目的构建,这样也是没问题的,不过初学者还是先使用官方提供的,比较这个人家都已经帮我们实现了,我们只需要简单的操作就可以了。
相关命令行操作如下:
npx create-snowpack-app new-dir --template [SELECT FROM BELOW] [--use-yarn]
// new-dir 项目地址
// [SELECT FROM BELOW] 选择你需要的项目
snowpack 提供了这是模板创建列表:
当前我们的是构建vue的项目,估我们命令行敲上:
npx create-snowpack-app my-first-snowpack --template @snowpack/app-template-vue
这个时候命令行展示如下代码:
- Using template @snowpack/app-template-vue
- Creating a new project in /Users/apple/Documents/learn/my-first-snowpack
- Installing package dependencies. This might take a couple of minutes.
这个时候项目目录就在构建中了,最后完成的反馈如下:
Quickstart:
cd my-first-snowpack
npm start
All Commands:
npm install Install your dependencies. (We already ran this one for you!)
npm start Start your development server.
npm run build Build your website for production.
npm test Run your tests.
创建之后的项目目录如下:
这个是官方提供的vue 模板文件,相对有些简单,那么我们可以根据自身项目的需求,去添加相关的页面。
这个时候我们就可以运行项目了。package.json 里面给我们提交了测试环境与线上构建的命令。
"start": "snowpack dev", //测试环境
"build": "snowpack build",//打包上线构建
构建项目之后,查看页面请求,我们发现了与我们以往不一样的项目目录。
当前页面index.html 就是项目中public中的index.html,而加载的资源就是index.html里面所引用的/_ dist _/index.js 里面的一系列资源,从加载的资源里面有
当我们改动代码的时候,你会发现页面热更新非常的快,几乎都是毫秒级别的。而这个优势就是我们要学习并使用snowpack的原因。
当我们执行:
npm run build
的时候,我们看到构建出来的文件目录如下:
目录文件说明如下:
文件解析:
env.js
export default {
"MODE":"production","NODE_ENV":"production","SSR":false};
这些是snowpack cli 自定义的默认环境变量,这些环境变量我们可以类似 process.env.XXX 这样在代码中去使用。不过snowpack 官网文档里面推荐的使用方式是:
//环境变量引用
data() {
return {
publicName: import.meta.env.SNOWPACK_PUBLIC_XXXX
}
}
而官方认可的环境变量编写方式必须是:
SNOWPACK_PUBLIC_*
//比如
//SNOWPACK_PUBLIC_ENABLE
//SNOWPACK_PUBLIC_ENV
//...
_ dist _ 业务代码文件集合
里面的App.css.proxy.js 这个我们查看源码:
const code = `\n.App {\n text-align: center;\n}\n.App-header { ...`
const styleEl = document.createElement("style");
const codeEl = document.createTextNode(code);
styleEl.type = 'text/css';
styleEl.appendChild(codeEl);
document.head.appendChild(styleEl);
我们发现这个就是app.vue 里面的 CSS 资源文件,通过javascript的方式被引用到了页面上。
web_modules
里面的文件就是 node_modules 里面引用的文件,其中import-map.json 就是里面的源码如下:
{
"imports": {
"vue": "./vue.js"
}
}
如果你的项目里面有其他的第三方引用,那么这个.json文件就会不断的添加,这个.json具体干什么用的。因为web_modules是我们的npm依赖映射的js文件,同时会有一个import-map.json来描述npm包与js的映射关系。
从snowpack 给我们构建的基础项目来看,距离我们完成的项目搭建还是有些缺失的。我们发现不管时snowpack dev还是snowpack build ,构建出来的项目里面js的引用:
<script type="module" src="/_dist_/index.js"></script>
你们发现这些的写法就是ESM的直接引用,而我们现实项目实践开发的环境,在移动端的开发,或者PC端的开发中,这样的写法在有些手机或者PC端电脑上并不支持这样的引用。
因此我们需要把我们的项目代码在部署到线上环境的时候,我们还是需要将其转换成es5的写法。这个时候snowpack 官方提供了插件给我们使用:
@snowpack/plugin-webpack
使用的方式:
npm install @snowpack/plugin-webpack --save-dev
然后snowpack.config.js文件里面的:
plugins: [
'@snowpack/plugin-webpack',
]
如果我们需要使用postCSS,Tailwind等相关引用。大家可以去查看官方给出了的引用方式。第三方snowpack插件支持列表,这里就不一一举例了。
使用@snowpack/plugin-webpack 最后构建出来的文件,就是跟我们使用webpack打包生成的文件一样的。具体大家可以查看一下plugin-webpack插件官网
看我们的项目依赖里面或者snowpack.config.js里面都有@snowpack/plugin-dotenv
这个插件就是给我们配置环境变量的,我们在项目开发过程中,不可避免的还是需要区分测试环境与线上环境的。毕竟我们项目开发过程中的测试环境AJAX域名与线上环境肯定不一样的,而我们业务场景下肯定是需要做区分的,那么我们需要去配置符合我们自身业务的环境变量。
但是我们发现,在snowpack给出的基本项目里面,不管是官网文档,好像就只有 dev 与 build ,那么就有一个问题就是:我们测试环境我们使用的ESM的模式去加载页面的,而现实环境我们是使用webpack 进行打包配置的。那么这样会导致测试环境的代码与线上环境的代码是不一致。这样不符合我们的开发规范。
那么我们需要本地开发的环境使用ESM,而测试环境与线上环境的代码必须是一直的。
那么我们选用一个测试环境的构建,那么我们应该如何去操作呢?
首先我们需要安装环境变量插件 cross-env
npm install cross-env --save-dev
package.json 新增代码:
"buildTest": "cross-env NODE_ENV=buildTest snowpack build",
配置环境变量
这样我们就可以区分环境,也就满足我们的实践项目开发需求了。
从基础项目的搭建,官方只给出了一个index.html的模板,如果我们需要使用路由是做单页面应用的,那么你们只需要引用 "vue-router": "^4.0.1"
就可以了,具体vue-router的使用方式查看官网vue-router-next
单页面的解决方案已经有了,但是多页面的呢?
我们在实际开发过程中还是会存在这种需求的。
实际操作起来很简单,只需要的src目录下新建如下文件:
.
├── about
│ ├── App.vue
│ └── about.js
└── home
├── App.vue
└── home.js
有个注意事项:
home 与 about 里面的 .js 文件名称不能相同的哦!
个人建议是.js 文件与文件夹的名称一致最好。
然后我们需要在public 中新增about.html,并修改index.html里面的引用。
//index.html
<script type="module" src="/_dist_/pages/home/home.js"></script>
//about.html
<script type="module" src="/_dist_/pages/about/about.js"></script>
我们只需要增加需要的页面文件即可.可能大家对于加一个页面需要加一个文件夹与.html页面,这样的操作比较繁琐与麻烦,那么我们可以写个自动化去添加这些文件,只需要
在npm add XXX
那么系统就会自动在src 下生成XXX文件以及public下生成 XXX.html, 这个很简单,我们可以使用 plop
定义命令语句,写个模板文件就可以了。具体的写法大家自己去查看plop官方文档 这个有很多的实现方式,我就不做介绍了。
比如 .eslint ,prettier,…其他我们项目常用的一些代码规范与git提交代码检查等等,这些都按照以前的来就可以了,与snowpack 本身并没有关系的 。
snowpack.config.js的配置文件如下:
module.exports = {
mount: {
public: '/',
src: '/_dist_',
},
plugins: ['@snowpack/plugin-vue', '@snowpack/plugin-dotenv'],
install: [
/* ... */
],
installOptions: {
/* ... */
},
devOptions: {
/* ... */
},
buildOptions: {
/* ... */
},
proxy: {
/* ... */
},
alias: {
/* ... */
},
};
其他的配置,大家可以查看官网说明,我这边不做具体的介绍。
主要简单说明一下:mount 与 plugins
mount 里面的参数配置结构如下:
mount: {
[path: string]: string | {
url: string, resolve: boolean, static: boolean, staticHtml: boolean}
}
这些选项配置,告诉Snowpack要构建哪些项目目录以及如何构建它们。定义以下示例配置,可以预期得到结果:
mount: {
public: '/',
src: '/_dist_',
},
其他 public 文件下的就是构建 .html 文件的目录所在,
src 构建的就是.html 所依赖的资源所在。而后面的 /
与 /_dist_
就是 npm run build
之后文件构建完成所在的目录,这个我们从构建出来的文件目录结构就可以看的出来。
从官网的这个配置文件的文档来看,mount 下面是可以无限添加的,也就是说我们可以自定义目录的,比如我想把.html的文件不放在public的目录下,我要单独提取出来管理操作,那么你可以在src 同层级的目录下新建 test 文件,在test文件夹下新增test.html, 那么我们可以配置 :
test: {
url: '/', static: true},
参数解析:
这样test/test.html就被打包好的。那么这个test.html 访问的页面就是xxx.com域名/test/test.html
就是这么的神奇,我们可以对所以的页面进行自定义处理。
具体上述三个参数不一样的设置会导致什么样的结果,大家可以去尝试一下。
plugins 就是我们所有第三方的插件使用,所配置的。
'@snowpack/plugin-webpack',
'@snowpack/plugin-vue',
'@snowpack/plugin-sass',
'@snowpack/plugin-dotenv',
这里面的插件,也包括我们自身去编写的snowpack 插件引用。
// my-first-snowpack-plugin.js
module.exports = function (snowpackConfig, pluginOptions) {
return {
name: 'my-first-snowpack-plugin',
config() {
console.log('Success!');
},
};
};
// To use this plugin, add it to your snowpack.config.js:
//
"plugins": [
["./my-first-snowpack-plugin.js", {
/* pluginOptions */ }
]
// ]
具体自定义插件的开发,查看官网插件开发
上述配置的项目仓库地址:
https://gitee.com/tootem9/snowpack-vue
当前仓库的内容,与我上述的描述基本一样的。只是初步项目的搭建与snowpack 这种基于ESM的探索与学习。不过使用snowpack 来构建项目还是会存在一些局限性,因为我们以为的项目开发中所依赖的第三方,就是我们 import 进来的模块引用需要指出ESM的才可以被snowpack所使用。尽管ESM已经被几乎所有浏览器所支持,不然不是同一个支持ESM的标准,会导致snowpack 构建失败的。不过随着ESM的往后发展,会有越来越多的第三方插件支持。并且snowpack 代表的 bundleless 方案肯定是光明的未来,带来的构建提效非常明显,而过程化体系发展中的历史包袱,仍然不适合直接在生产环境使用的。所以对于这类方案我们还是需要根据实际情况来决定是否使用。
参考链接: