解决方案 Solution
你需要的,还是 JavaScript 和 Web API。我希望阁下可以看看 Web API (detailed),快速浏览一下然后回到本文。
What you need is vanilla JavaScript and the Web API. I want you to take a look at the Web API (detailed). Scimm through it for a couple of minutes and come back.
看完了?不错。我第一次细读之后,大为惊叹。去掉很多东西变得很简单。好比说 MutationObserver,通过它你观察 DOM 变化,那么做起数据绑定和 DOM 交互就非常简单。
Done? Awesome. I was pretty impressed when I first looked at it in detail. A lot has changed and it's much simpler to work with. Take MutationObserver for example - it allows you to watch parts of the DOM for mutations. This feature alone would allow you to setup simple databinding and react to potential changes.
举个例子,观察 h1 可编辑标签。
Here's an example of observing a contenteditable
h1
tag:
let elementToWatch = document.querySelector('h1')
elementToWatch.contentEditable = true
let observer = new MutationObserver(mutations => {
mutations.forEach(mutation => {
console.log(mutation.target.textContent)
})
})
observer.observe(elementToWatch, {
subtree: true,
characterData: true
})
这算是没有的例子,你宁愿观察 DOM 结构是怎么变化的,
This is a fairly useless example, and you would rather want to observe DOM structure mutations with it, but you get the idea.
另外一个例子是 Fetch API,比 XHR 更强大、简单。
Another example is the Fetch API. It's like XHR, but much more straight-forward and powerful.
更新 UPDATE (30/4 17:30 CET):
我回想起我六年前那时候开始搞 Web 开发,彼时的 Web API 和 DOM API 确实令人头疼。时光荏苒,ECMAScript 已经变化很多。首先是语法部分改进了很多,其次是 Web API 的实用性也增强了。开始的时候之所以你会使用这些框架是因为你觉得有许多新玩意,其实大多数都通过 polyfill 实现的。
When I first started web development 6 years back, the Web API & DOM API were indeed a pain to work with, but times have changed and the ECMAScript updates alone has turned the tables quite a bit. Syntatical changes has been made to the language that plays much nicer with some parts of the Web API that were previously non-trivial to work with. New features are constantly being rolled out that accounts for many of the reasons you are working with a framework in the first place, most of which can be polyfilled where not available.
新语言特性 Proxy 可以实现 A 到 Z 的交互。
The new Proxy language feature allows you to implement reactivity from A to Z, among other things.
Decorators 可以部分地添加你代码的功能。
Decorators allows you to add additional functionality to parts of your code.
类继承可让你的程序像演化那样,从基础一直发展下去。
Class inheritance allows you to start from a base and extend additional features to your app as it evolves.
还是那句话,这是你程序需要不需要的问题。其实这可以不要的,甚至不需要装饰的,但是这没意义,浏览器本身提供好的功能你调用就是了,不需要再你程序多此一举引入其他库。
This again, is a question of what your application needs. It might not even need reactivity, it might not even need to decorate functionality - but nontheless it is there, in the browser and available to you whenever you need it without any additional overhead to your application (unless polyfilled).
这篇文章几乎变成围绕“如何将你框架替换成为普通 JS”,但我想说的这并不是我的原意。我原意是反思下使用框架的原因。问问自己是否真的那么复杂。
This post has blown up to becoming "how you replace your framework with vanilla JavaScript", and that was not the intention. The idea is that you should take a moment and reflect on your reason for using a framework. You have to ask yourself if your application really is all that complex that you absolutely require solutions to the vast amount of problems a framework tries to solve.
实际上一开始没那么的问题,只不过你想了才会有。
You most definitely don't have many of those problems in the first place if you think about it:
-
把状态反射到 UI 没有问题的,只不过你想了才会有。You don't have problems reflecting state onto the UI if you think about it.
-
数据保存和缓存没有问题的,只不过你想了才会有。You don't have problems with data storage or caching if you think about it.
-
我写的可复用组件和代码没有问题的,只不过你想了才会有。You don't have problems with writing reusable code/components if you think about it.
-
程序实际没那么庞大,UI 没那么复杂,只不过你想了才会有。You don't have problems with large, complex UIs if you think about it.
-
实际没那么多性能的问题,只不过你想了才会有。 You don't have problems with performance if you think about it.
公司之所以这样并不表示他们很高级,而且其中的程序员就是那么平庸,觉得使用框架就是对的。实际要不要框架还是取决于他们实际的需求。
Just because most companies are using frameworks doesn't mean they have to either. Companies aren't some higher form of being, they consist of developers and normal people that has made a choice of using a framework because they think it is the right to do. If it is a good choice or not depends on their application(s).
编写函数和类 Write functions and classes
在代码很常见的一件事情就是写函数和类。例如在服务端和 DOM 交互的时候,写一个观察者是有必要的;另外一个例子就是你要写组件的时候很自然地会写一个类。你完全可以利用 HTML5 History API 构建你自己的一个小型的路由器。
Things that you often use in your application should of course be placed in their own functions or classes, depending on if you need instances of something or not.This could for example be setting up observers, walking the DOM or talking with a server-side resource.A great example of using classes is if you want a lightweight implementation of components. An instance of a specific class can represent a component and be its view-model.You could also build a tiny router and utilize HTML5 History API.
也许有人认为这是在重复制作轮子。不过这还真不是,因为只有你一个在写,你当然拥有决定权怎么去写代码,只要你自己觉得舒服就好。你想怎么弄就怎么弄。
Some may argue that this is like reinventing the wheel, but it really isn't because you are the one in control. You are the author of your code, and you get to work with code you feel comfortable with. You can build it however you want and support whatever browser versions you want.
看看 router.js,README 如此说:
Take a look at router.js, this is what their README
says:
router.js 是个轻量级的 JS 库,以 route-recognizer
和 rsvp 的
为基础,提供路由处理的 API。
router.js
is a lightweight JavaScript library that builds on route-recognizer
and rsvp
to provide an API for handling routes.
库文件压缩后 24.4KB,转译后 2,175 行代码。
The library is 24.4KB minified, and 2,175 lines of code after transpilation.
你在你程序中你写一个路由器会写多少行代码?100?200?1000?20 行呢?
How many lines of code do YOU think that YOU can write a router for your application in? 100? 200? 1000? How about 20?
我不是说你不应该使用库,只是我希望你在选择库的时候要谨慎一些。看看源码,如果不需要的东西就要减少文件大小,或者试试提交一个 pull 请求。
I'm not saying that you shouldn't use libraries, because you should. But I want you to be critical when choosing what libraries to use. Check through the source, maybe submit a pull request and try to help reduce the size a bit if you find something that's unnecessary.
要知道何时适合用 js,何时适合 css Know when to use JS and when to use CSS
CSS 也很强大啦。很多动画和效果都不需要 JS 参与完成。建议不要用 height / width 而是 transform/opacity 做动画。另外可以参考这里。伪类 :hover/:active/:focus 在做下拉菜单时候很好用,可通过这些伪类触发事件,精确到每个子元素。
CSS is powerful too. It can handle almost every animation/transition that you can imagine without the need of any JavaScript involved.
Only animate the transform
and opacity
attributes, and never things like height
or width
. A bit more about this here.
Use :hover
, :active
and :focus
for simple triggers for something like a dropdown menu. You can also trigger child elements via these methods.
浏览器特性支持够吗?What about browser support?
前端项目的一个问题就是解决浏览器是否支持某项特性的问题。如果遇到不支持的做法就是采取 polyfill 方案(它是用来描述复制缺少的 API 和API 功能的行为。你可以使用它编写单独应用的代码而不用担心其他浏览器原生是不是支持)。然而有些像 Service Worker 的功能却是无法做到兼容,——这种情形,搞不定就是搞不定,换上框架也是爱莫能助。
Browser support is usually something you have decide upon a project-basis, but if there's a feature that isn't available for a browser you want to support, just polyfill it. Granted, there are some features like Service Worker that can't be polyfilled, but if a feature can't be polyfilled, a framework can't have support for it either.
关于 polyfill 还有个问题就是会增大程序尺寸。确实如此,不过好消息是,如果浏览器支持的就无须加载 polyfill 那部分代码。你可能又会说,这样会增加请求服务器的往返次数。不过还好,现代的 web 不存在这个问题了,下文再解释。
An argument that often comes up when talking about polyfilling is that it increases the size of your application. That is true, however you can dynamically polyfill based on what the client browser already has support for. And the follow-up argument is that dynamic polyfilling adds additional round-trips to the server, and that is however NOT true in modern web, explained below.