Anikyu 是由本人所封装的一个补间动画库,基于JavaScript,能够为一个指定对象中的数值建立连续补间动画。html
Anikyu 源于我平常所写的一个demo —— 使用JavaScript实现动画。代码参考Tween.js的功能进行编写,缓动函数来自ECharts。前端
Anikyu源代码仓库:anikyuvue
毕业后我来了北京。到如今做为正式员工(2020年4月),我去过两个公司,都是初创公司,用的技术栈基本都是Vue.js这一套,经过Vue CLI工具便可搭建一个脚手架(空白项目/项目模板),不用任何配置,在初始化事后,脚手架就能够被启动了,而后咱们能够在此基础上进行开发。node
vue init webpack
以后咱们进入项目目录,能够看到以下的文件结构:webpack
事实上,咱们在此关心的内容并很少,仅仅是了解一下目录结构,以及该项目使用到了哪些依赖包。git
参考自vuejs-templates webpackgithub
package.json包含有当前项目的信息。web
由此咱们能够了解到,咱们使用Vue CLI工具建立的项目:vue-router
在开发环境下须要的包不少(主要是和前端工程化相关的内容),大体概括一下包括几个大类:shell
注:在最终打出来的生产环境包中仅包含运行依赖项,不会包含开发依赖项。
上面咱们所看的是Vue脚手架初始项目的依赖,使用脚手架靠发的最终产品实质上是一个面向最终用户、基于Vue的项目,而咱们的目标是构建一个面向开发者的JavaScript的库。
事实上,咱们开发本身的库并不用像上面的脚手架同样须要不少依赖,准确来讲其实能够没必要任何开发依赖或是运行依赖。咱们彻底能够不借助任何前端工程化工具,手写代码,直接发布。惟一的问题是代码可能会稍显冗余,或不太严谨,或者无论遇到什么问题都得手动改代码。
因为前端工程化是趋势,本文的目的也主要是学习这些工程化工具。所以,借助上文中咱们所说起的一些工具:
咱们就能够构建一个无需任何运行依赖项便可运行的库。
既然有了对Vue脚手架初始项目文件结构及其依赖项的大体了解,咱们是否是就能够依葫芦画瓢,来实现一个本身的库呢?
这里,以我以前所写的Anikyu这个库为例来进行介绍。
cd 到将要用于存储项目的空目录
使用命令
npm init
填写一些信息,生成一个package.json
package.json 文件的所有内容已经展示在命令行窗口中,其中包含有在上文没有说起的一些字段:
import Anikyu from 'anikyu'
)这样咱们就建立了一个空白项目。
若是咱们不须要使用工程化工具,只需在当前目录下建立一个index.js文件(对于package.js的入口文件),而后将代码写在其中便可。
与此同时,别忘了在项目文件夹里初始化一个Git仓库,以确保项目文件出现问题时能够从仓库中找回。
能够将下文说起的相关开发依赖复制到package.json中devDependencies字段下,以后运行npm i
来直接安装;或者在命令行中手动运行 npm i --save-dev
+依赖名称 进行安装。(因为在中国直接访问npm的速度很慢,所以这里使用了cnpm)
"eslint": "^6.8.0"
ESLint 相关依赖以及配置能够先全局安装ESLint,而后进入项目文件夹,经过eslint --init
来初始化。
下图是初始化结束后发生的相关变化。
"eslint-plugin-import": "^2.20.1", "eslint-plugin-node": "^11.0.0", "eslint-plugin-promise": "^4.2.1", "eslint-plugin-standard": "^4.0.1"
若初始化配置的时候选择了相关内容,同时也会安装相关依赖(此处暂未了解,今天看项目的时候发现多了这四个依赖,建议先别复制)
"webpack": "^4.41.5", "webpack-cli": "^3.3.10", "babel-loader": "^8.0.6", "@purtuga/esm-webpack-plugin": "^1.2.1", "clean-webpack-plugin": "^3.0.0"
Webpack的核心;不知为什么彷佛两个都须要安装,不安装其中一个会报错?
用于在Webpack中对js文件使用Babel进行处理
用于将库打包为符合ES Module的包
打包以前将打包目标文件夹(/dist)进行清空
"@babel/cli": "^7.8.3", "@babel/core": "^7.8.3", "@babel/polyfill": "^7.8.3", "@babel/preset-env": "^7.8.3"
(暂未了解)
经过上文咱们对Vue CLI脚手架的了解,咱们也能够在咱们的库中规划出文件结构。
Anikyu是这样规划的:
src/ - 写Anikyu代码的地方
polyfill/ - polyfill相关代码
Anikyu库代码使用ES6语法来进行组织,例如引入(import ... from ...)、导出(export ...)、类(class)。
因为Anikyu核心代码以前已经写好(我这里就再也不从头写一遍了),所以咱们能够将项目中src目录拷贝到新项目根目录下。
同时也修改一下package.json中的入口文件为src/anikyu.js。此时这就是一个未进行打包的、由不少零散文件所组成的库。若是你的浏览器支持运行ES Module,那你将可以在浏览器中直接运行这个库。
执行器是Anikyu计算补间的核心,目标对象中值的计算、改变,以及事件的触发也由执行器来进行。
缓动函数来自ECharts中的相关示例。
Anikyu是一个动画对象,那对于动画状态的监听(例如监听动画帧的请求、动画的结束)使用和事件相相似的机制会更好一些。
Anikyu类基于EventDoer类,继承关系如图所示:
EventDoer相似浏览器中自带的EventTarget对象,能够为Anikyu对象添加事件监听。当Anikyu示例的某一动画阶段正在请求帧或是播放完成的时候,可以触发相关事件监听函数。
Anikyu类则用于控制动画的播放过程,包括暂停、继续、废弃等等。
包含了一些经常使用工具,如计算CSS实际值、事件触发、生成范围内随机数、数值限制、时间获取。
当前仅包含了requestAnimationFrame的polyfill,以兼容IE9浏览器。
该文件是Anikyu的出口,用于进行混入polyfill等操做。
若是你早前对Webpack进行过全局安装(即只需在运行框/cmd.exe中输入webpack不会报找不到命令),那在这一步骤中,你只需在命令行中输入:
webpack ./src/anikyu.js
便可完成打包,打包好的文件默认保存在dist目录下,文件名为main.js。
经过这种方式打包,咱们发现如下几个问题:
所以,咱们还须要对Webpack进行深刻配置。
( 参考[Webpack官网 - Authoring Libraries
](https://webpack.js.org/guides... )
此时,咱们在根目录建立一个webpack.config.js,在其中写入Webpack配置。
这里的module.exports
能够接收一个配置对象(只打包一个文件),也能够接收由多个相似的配置对象组成的数组(打包多个文件)。这里咱们建立Anikyu 一种版本的两个文件 —— 通过压缩的文件(anikyu.min.js)和未经压缩的文件(anikyu.js)。两个文件都符合umd规范,即可以在不支持ES Module的浏览器中直接运行,区别仅在于代码是否被压缩。
咱们看一看配置对象,以下是未压缩的UMD版本的配置。
{ entry: './src/anikyu.js', output: { path: path.resolve(__dirname, 'dist'), filename: 'anikyu.js', library: 'Anikyu', libraryTarget: 'umd', libraryExport: 'default', globalObject: 'this' }, mode: 'production', optimization:{ minimize: false } }
output - 输出配置
optimization - 打包优化
以后咱们在根目录下,不带参数直接执行webpack
命令,文件便可开始打包。
上一步中打的包符合umd规范,可以在浏览器中经过传统的script标签进行引入。但根据个人观察,不少类库(如Vue.js、Three.js)都提供了支持ES Module的包,事实上这彷佛也正在成为一种趋势。通过本人各类百度,貌似让Webpack打出ES Module包的方法是引入EsmWebpackPlugin
扩展(来自@purtuga/esm-webpack-plugin包)。
咱们将该扩展引入到webpack.config.js中
在plugins字段中引入,libraryTarget改成var。以后咱们再进行打包,便可打包出ES Module包,实际测试,一切正常。
在以前安装依赖的过程当中,咱们已经安装过了babel-loader
,和其它各类各样的loader同样,它处理的是js文件(虽然Webpack原生支持处理js,但相关不兼容老旧浏览器的代码并无通过转译过程)。
在webpack.config.js中的module字段里添加rule,表示赶上js文件时就使用babel进行处理。
我认为,在浏览器环境下,原生支持ES Module的浏览器必然也支持Anikyu中所使用的相关ES6特性,所以ES Module包我没有使用Babel,仅对umd包使用Babel。
这是Anikyu的配置,但对于其具体配置详情本人目前暂无了解。
请参阅 Config Files · Babel 。
{ "presets": [ [ "@babel/preset-env", { "useBuiltIns": "usage", "targets": { "browsers": ["last 2 versions", "ie >= 9"] } } ] ] }
在配置完成后,咱们能够从新运行webpack进行打包,此时Babel就能够对不兼容老旧浏览器的代码进行转译,使得库可供不支持ES6等特性的浏览器使用。
行文至此,Anikyu库的开发、打包实际上已经能够告一段落,但开发过程当中有的地方还能够继续优化,例如代码风格可能还不够规范、每次运行打包都要输入webpack不太方便。
那接下来的步骤咱们就对这些细节进行优化。
上面的步骤中咱们已经安装好了ESLint,生成了.eslintrc.js这个配置文件,其中的配置都是在执行初始化ESLint命令后根据你的选择所生成的,此时ESLint规则便已经生效。
我在该文件中的“rules”加入了额外的一些规则,以符合我本身写代码的习惯。
"rules": { "indent": [ "error", "tab" ], "linebreak-style": [ "error", "windows" ], "quotes": [ "error", "single" ], "semi": [ "error", "always" ], "space-before-function-paren": 1, "space-infix-ops": 1, "spaced-comment": 1 }
若有文件无需被ESLint检查,可在.eslintignore里设置忽略。
在咱们平常开发项目过程当中,例如咱们要打包一个项目,通常会执行 npm run build
,而不是手动执行webpack
。要对此进行配置,咱们须要修改package.json中的script。
{ "test": "echo \"Error: no test specified\" && exit 0", "lint": "eslint src --ext js", "build": "webpack-cli" }
在这里,咱们添加了lint和build两个脚本,原有的test脚本因为我不会配置,因此先让它 return 0
。
到此,Anikyu库的开发已经结束,假设如今通过测试,一切运行正常,咱们就能够对包进行发布。
npm login
,输入登陆凭据来登陆npm publish
,便可将包发布到NPM(尴尬了,刚刚不慎把这里的demo版本发布出去了,原本当前线上版本是0.2.2,这里初始化之后默认版本是1.0.0,忘改了;不过还好我及时用 npm unpublish --force
撤回了刚刚的发布)
今后,世界各地的人将可以经过npm install anikyu --save
来安装Anikyu依赖。
我想起啥的时候就写些啥吧。。。
早期开始作这个库的时候,我试过直接在配置中传入函数做为参数,例如:
new Anikyu({ onAnimate: function(){...}, onFinish: function(){...} })
但这样作存在的问题是,若是须要在事件被触发后执行多个函数,这种方式不是很灵活。
正如好久之前在DOM文档里写相关事件处理函数:
window.onload = function (event){ ... }
所以我尝试让Anikyu直接继承浏览器自带的EventTarget对象(该对象提供了咱们所熟知的.addEventListener等方法)。在不一样浏览器上进行测试后,发现任何版本的IE浏览器都没法经过 new EventTarget()
的方式来调用。在继续测试、查阅文档过程当中,发现EventTarget类并不可以支持Anikyu所需的全部API。
最终我编写、模拟了一个和EventTarget类类似的EventDoer类,由Anikyu类继承。
在Anikyu实例上可这样调用:
let ani = new Anikyu(...) ani.addEventListener('animate',function(e){ ... })
参考自EventTarget - Web APIs | MDN
前期没作过深刻了解,只是发现Webpack用途普遍(平常项目以及招聘信息等不少地方都提到这个,顺便也学一下),因而就尝试使用Webpack来进行打包。但Webpack彷佛有个问题,在IE8下,某个地方会提示没法使用Object.defineProperty方法(可能和Vue.js不支持IE8是同一个缘由),致使报错。但我本身写的代码里彷佛没用到Object.defineProperty方法,定眼一看,代码彷佛来自于Webpack(后来在官网发现Webpack的确只可以兼容到IE9),遂考虑更换一个打包工具。
到后面瞄了一下Three.js、Vue.js和ECharts的打包工具,用的都是Rollup。在某个分支里我也对其进行了配置,但问题就在于:
最终,遂暂时弃疗Rollup。
历时两天,整篇文章终于写完了。这大概就是从0到1搭建一个前端工程化项目的过程吧。不过本人目前仍是处于很菜的状态,不是很肯定上文的相关表达有没有很准确、很通俗易懂。若是你对Anikyu这个库很中意,不妨拿来用一用吧。发现问题,欢迎提issue。
Anikyu是一个面向本人兴趣编程的项目,而不是面向公司KPI编程的项目。