vue-next(3.0)版本开源已经很长时间了,虽然还没有发布正式版,但不影响我们在自己的项目中尝试使用。本文是对自己的「电影预告片」项目刚刚用vue3重写后的总结。
项目演示地址
项目介绍
我在掘金上发布的第一篇文章就是关于此项目的介绍,没想到已经过去俩年了,回头看看这俩年的成长… 打死自己的心都有了!!!🙃
项目整体的视觉/交互效果没有做改变,如下:
前端项目
项目创建
-
使用
vue-cli
工具可以方便的创建一个vue项目,其中选择技术栈时,勾选上TypeScript就好。 -
项目初始化好,通过
vue add vue-next
安装官方插件,使其vue版本升级到vue3.0 -
修改
shims-vue.d.ts
文件,引入.vue
组件,类型是defineComponent
函数返回的类型declare module "*.vue" { import { defineComponent } from "vue"; const Component: ReturnType<typeof defineComponent>; export default Component; }
涉及到需要改变的点
-
移除
filters
过滤器特性,官方原因是说filters
功能完全可以用函数、计算属性来完成,没必要增加学习成本,并且|
与位运算符是冲突的,同时还增加了模板解析复杂度。具体可以看 rfc -
修改了
v-model
的API,同时移除了model
选项。在之前我们开发组件实现双向绑定,是通过props.value
,调用this.$emit('input')
来实现双向绑定。vue3.0将使用modelValue
代替value
,emit('update:modelValue')
来代替input
事件。具体可以看 rfc
<!-- 使用组件 -->
<Comp v-mode="text" />
// modelValue 通过 attrs 获取,emit用于触发事件
export default defineComponent({
setup(props, { attrs, emit }) {
const onChange = (e) => {
emit('update:modelValue', e.target.value)
}
return { onChange }
}
})
-
Transition
组件的className
改变,之前过渡效果是写在xxx-enter
、xxx-leave-to
css类名中,现在xxx-enter
改为xxx-enter-from
。具体可以看 rfc -
插件install的参数改变。之前写一个插件,可以直接使用install方法的入参
Vue
,将方法或属性挂载到Vue.prototype
上。 现在install方法的入参是app。
// 之前插件挂载属性的方式
export default {
install: (Vue, option) => {
Vue.prototype.$axios = xxx;
}
}
// vue3.0的方式
export default {
install: (app, option) => {
app.config.globalProperties.$axios = xxxx
}
}
// 另一种方式 也可以使用 provide 、inject , 在composition API中使用
export const symbolKey = Symbol('_axios_');
// setup 函数里使用composition API
export function useAxios() {
return inject(symbolKey)
}
// 执行插件insall方法注入
export default {
install: (app, option) => {
app.provide(symbolKey, xxx)
}
}
其实vue3.0
版本改动很多,但因为项目不是很复杂,所以涉及的不是很全面,大家可以去阅读rfcs查看改变的一些讨论。
Composition API的应用
该项目所有组件都使用了 Composition API
开发,基于 Composition API
我们可以更好的封装公用逻辑。
请求函数的封装
在此之前,项目的异步请求的loading要额外定义和维护,有了 Composition API
我们可以封装一个类似于React swr
取数库的hook函数。
// 类似于这样
export function useRequest(url, params, config) {
// 统一维护的变量,最后return出去
const state = reactive({
loading: false,
error: false,
data: config.initialData
})
const fetchFunc = () => {
state.loading = true;
// 做请求的公用逻辑
axios().then(response => {
const result = response.data;
state.data = result.data;
state.loading = false;
}).catch(err => {
state.error = true;
})
}
onMounted(() => {
if (config.immediate) {
fetchFunc();
}
});
// toRefs 可以将state,拆解成多个ref,这样调用者就可以使用解构来拿到变量
return { ...toRefs(state), fetch: fetchFunc };
}
事件的封装
比如 touch
事件,我们不光要绑定事件和解绑时间,还需要在不同回调中共用一个副作用变量,我们就可以把它拆成 composable
函数
// 类似于这样
export function useTouch(domRef, ref) {
// 是否touch进行中标志位
let initiated = false;
// 监听domRef改变
watch(domRef, (el, prev, onCleanup) => {
const touchStart = (e: TouchEvent) => {
e.preventDefault();
initiated = true;
callbacks.touchStart(e);
};
const touchMove = (e: TouchEvent) => {
e.preventDefault();
if (!initiated) return;
callbacks.touchMove(e);
};
const touchEnd = (e: TouchEvent) => {
initiated = false;
callbacks.touchEnd(e);
};
el.addEventListener("touchstart", touchStart);
el.addEventListener("touchmove", touchMove);
el.addEventListener("touchend", touchEnd);
// 取消绑定
onCleanup(() => {
el.removeEventListener("touchstart", touchStart);
el.removeEventListener("touchmove", touchMove);
el.removeEventListener("touchend", touchEnd);
});
})
}
优化
增加了travis实现了提交自动构建发布,通过sshpass实现将静态资源发送到远程服务器。
language: node_js
node_js:
- 12
branchs:
- master
addons:
apt:
packages:
- sshpass
install:
"npm install"
script:
- "npm run build"
after_success:
- ./script/deploy.sh
其中 serverPass
和 serverIP
都是在travis上配置的环境变量。
#!/usr/bin/env sh
# 确保脚本抛出遇到的错误
set -e
# 打包静态资源
npm run build
# 将dist文件发送到远程
sshpass -p ${serverPass} scp -o stricthostkeychecking=no -r dist/ root@${serverIP}:/home/web/movie-trailer
后端项目
后端从使用 koa
框架升级成 Egg
框架,使用 Koa
开发应用时,需要开发者下载或开发各种中间件来实现功能的增强,而 Egg
选择了 Koa
作为其基础框架,帮助开发者对其进行了一些增强。
Egg
除了提供了 Controller
、 Service
层做业务处理之外,还提供了 extend
用于扩展自身的功能(多种扩展点)。比如我们可以扩展 Context
,在其上下文上挂载返回结果的通用函数。
// app/extends/context.js
module.exports = {
sendSuccess: function(result) {
this.body = {
code: 200,
errMsg: '',
data: result
}
},
sendError: function(errorMsg) {
this.body = {
code: 300,
errMsg: errorMsg,
}
}
};
// app/controller/keyword.js
class KeyWordController extends Controller {
async index() {
const { ctx, service } = this;
const keywordList = await service.keyword.index();
// 返回200成功
ctx.sendSuccess(keywordList)
}
}
在此之前,我是通过Python进行爬取豆瓣电影,然后通过crontab手动写入脚本, Egg
框架方便了开发者设置定时任务,使用了 Egg
, 我们只需要在 app/schedule
目录下创建文件导出基于Subscription
的类。
class CrawlingDouban extends Subscription {
// 静态方法,定义执行时机
static get schedule() {
return {
cron: '0 8 * * *', // 每天8点
type: 'worker',
};
}
async subscribe() {
// 爬取逻辑
}
}
总结
重构过程中,还是会遇到一些问题,大部分是文档没读导致的,所以文档过一遍还是很重要的。希望此文章对大家有所帮助,感觉项目还算可以的,不要吝啬start哦!。
参考
版权声明:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 作者: lihaozecq 原文链接:https://juejin.im/post/6862582779419459598