简介
Svelte是最近新出的前端框架,和react的vdom不同,Svelte使用静态分析在构建时创建DOM更新代码,搭配官网一目了然的新手教程,给人直观的使用体验,上手会很快。在业务实践中我上手采坑并总结了一下内容,经实践可以直接上生产。
ts+scss+svlete模板
通常在react项目里我们会用ts提高业务逻辑开发效率,style module用来避免css命名重叠。在react中可能是这么写
// index.tsx
...
<ul className={styles.mylist} ref={this.myRef} onScroll={this.scrollHandler}>
{this.props.data.length>0
?this.props.data.map((v,i)=>
<Item key={i}>{v}</Item>
)
:<li>empty</li>
}
</ul>
...
在svelte直接单文件组件
// index.svelte
<script lang="ts">
...
import type {IData} from "./types.ts"
import Item from "./item.svelte"
export let data:IData=[]
export let myRef;
scrollHandler=()=>{}
</script>
<style>
...
</style>
<ul class="mylist" bind:this={myRef} on:scroll={scrollHandler}>
{# if data.length>0}
<li>empty</li>
{:else}
{#each data as v,i}
<Item>{v}</Item>
{/each}
{/if}
</ul>
需要注意:
1.凡是svelte组件import的名称必须首字母大写,不然编译不认它是个svelte组件。
2.import ts类型声明必须是 import type ... 也是为了更好的区分.
export let/const
变量 作为类似props的语法,目前语法检查还不完善,有时会出现警告- 和React不同,循环的dom不强制检查key属性
- svelte单文件组件中的css会天然支持编译成类style module的className,不用担心css名称重复,但会忽略和警告未使用的css,不会打包出来。(这个很奇怪,如果父组件和子组件想覆盖对方的css classname 就比较麻烦了,这时候纯css思路断了只能换js思路也就是通过props来实现)
状态管理
组件内的状态没什么好说的,临时变量一把梭就好了。本文只讨论store的用法。为了让svelte状态管理有redux的味道,我实践中用以下的写法,不喜勿喷:
//store.ts
import { writable } from 'svelte/store';
//声明初始state
let initState={
loading:false,
readed:0,
}
const state = writable(initState);
const { subscribe, set, update } = state;
//可能用得到的reset
const reset = () => set(initState);
//mounted方法会在组件的onMount时调用
const mounted = async () => {
updateLoadStart();
const {data,error}=await server.getPageData();
!error && await updateReaded(data);
updateLoadEnd();
}
updateLoadStart=()=>update((prevState)=>{
return {
...prevState,
loading:true
}
})
...
在组件里便可以这么引入使用:
<script lang="ts">
import {state,mounted, onBtnClick} from "./store";
onMount(mounted);
</script>
<button on:click={onBtnClick}>{$state.readed}</button>
实践中你可以大胆的拆分和命名 比如类vue的命名 mounted method compute的数据管理,又比如类rematch的 select loading.effects 组合,具体看之前框架的习惯过度一下~
需要注意:
- 目前没有devtool的浏览器插件可以查看修改state,我反正安装了个svelte的插件试了啥都没有,可能未来不久会发布吧。目前只能{@debug $state} 来观察state的变化
与dom的交互
我看到 https://css-tricks.com/lazy-l... 一步步教学写个图片懒加载的组件。着实不错,改一下兼容问题可以上生产。
其次官方文档的motion/transition/animate 我试过可以很方便实现一些常见动画功能,比如动画折叠展开、轮播的滚动,这里就不贴代码了,跟着文档走一遍基本没难度。
注意的点:
1.on:click 这种直接透传事件的貌似只在组件的根元素起作用,在子元素里还是得用on:click={() => dispatch('click')} 的方式向上emit
2.html标签校验竟然认为 href="javascript:" 不是合法的链接,必须要在上一行注释<!-- svelte-ignore a11y-invalid-attribute -->才行
3.样式写法习惯上class="" style=""更接近vue,而不是react的 className="" style={}
4.bind过的ref记得onDestory时销毁
打包
官方给的rollup打包配置过于简单了,也不兼容es5。网上看到有其他同学重新编译svelte到es5的,也是厉害。其实魔改一下官方的rollup配置就行。我实践中可以愉快的把ts编译成es5。
简单讲一下几个要点,最后会放出配置文件给大家参考,少走弯路少采坑。
1.打包资源加hash
...
output:{
...
entryFileNames: "bundle.[hash].js"
}
- 向下兼容
"> 1%, not dead" 为了兼容连箭头语法都不支持的浏览器,选择个,文件体积会加倍,一个页面差不多200k gzip 60k
"> 5%, not dead" 这个就小了好多 100k+ gzip 30k+
3.用"rollup-plugin-serve"换掉官方手写的serve
!production && serve({
contentBase: "dist",
port: 5000
})
- svelte css 加hash
css: (css) => {
const hash = crypto.createHash("md5").update("bundle" + css.code).digest("hex");
css.write("bundle." + hash.slice(0, 8) + ".css", !production);
}
当然如果想postcss autofixer进一步处理的网上也有其他答案
5.用@rollup/plugin-html 根据模板替换hash的文件并生成manifest
html({
dest: "dist",
fileName: "index.html",
template: async ({ bundle }) => {
let bundleFileNames = Object.keys(bundle);
fs.writeFile('dist/asset-manifest.json', JSON.stringify(
bundleFileNames.reduce((assetMap,fileName) => {
assetMap[fileName.replace(/\.\w+\./, '.')] = fileName;
return assetMap
}, {}),null,"\t"),"utf8");
return bundleFileNames.reduce(
(code, fileName) =>
code.replace(fileName.replace(/\.\w+\./, '.'), fileName),
await fs.readFile("public/index.html", "utf8")
);
},
})
个人感悟
感觉svelte体验上模板语法和单位件组件的思路更接近vue,但又不完全遵循指令规范(比如循环判断)加入了精简但又实用的 动画 和 状态管理。其次性能方面比react好一点点,打包体积小是有很大优势。期望未来在开发体验比如工具链上有新的突破。
框架对比?
https://medium.com/@d_kzlv/yeah-holy-wars-i-like-that-1406eb291331 发现medium大家意见不一,看看就好
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。