使用vue3+ts(x)作为基础
<type>(<scope>): <subject><BLANK LINE><body><BLANK LINE><footer>
{
"gitHooks": {
"pre-commit": "npx @ls-lint/ls-lint && lint-staged",
"commit-msg": "commitlint -e $GIT_PARAMS"
},
"lint-staged": {
"*.ts?(x)": [
"npm run lint-staged:js",
"prettier --parser=typescript --write",
"git add"
],
"*.{js,jsx}": ["vue-cli-service lint --fix", "prettier --write", "git add"],
"*.{vue,css,scss}": ["stylelint --fix", "prettier --write", "git add"]
}
}
// 区分场景 一个是公用组件 一个是项目内置组件,共有组件按需加载,内置组件自动注册,
// 组件类型 标签类型组件 函数类型组件
const context = require.context('./components', true, /\.tsx$/)
const componentMap = new Map()
context.keys().forEach(k => {
const componentConfig = context(k).default
if (componentMap.has(componentConfig.name)) {
console.warn('componentName has used')
} else {
app.component(componentConfig.name, componentConfig)
componentMap.set(componentConfig.name, componentConfig.name)
}
})
app.mount('#app')
// 函数挂载
app.config.globalProperties = {
$message: Message,
}
。。。。在大型项目的长期维护与迭代中,ts所有的特性都能很好的满足这个场景
<!-- 性能编译,样例模板 -->
<div>
<span>Hello World!</span>
<span>Hello World!</span>
<span>{{mes}}</span>
<span @click="handlClick">click</span>
</div>
// 示例如下 ,自己可以到网站上面试一下
import {
createVNode as _createVNode,
toDisplayString as _toDisplayString,
openBlock as _openBlock,
createBlock as _createBlock,
} from 'vue'
const _hoisted_1 = /*#__PURE__*/ _createVNode(
'span',
null,
'Hello World!',
-1 /* HOISTED */,
)
const _hoisted_2 = /*#__PURE__*/ _createVNode(
'span',
null,
'Hello World!',
-1 /* HOISTED */,
)
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (
_openBlock(),
_createBlock('div', null, [
_hoisted_1,
_hoisted_2,
_createVNode('span', null, _toDisplayString(_ctx.mes), 1 /* TEXT */),
_createVNode(
'span',
{
onClick:
_cache[1] ||
(_cache[1] = (...args) =>
_ctx.handlClick && _ctx.handlClick(...args)),
},
'click',
),
])
)
}
// Check the console for the AST
// 新写法在代码量和逻辑切割上面更有优势
function Counter({initialCount}) {
const [count, setCount] = useState(initialCount);
// 相当于 componentDidMount 和 componentDidUpdate:
useEffect(() => {
// 使用浏览器的 API 更新页面标题
document.title = `You clicked ${count} times`;
});
return (
<>
Count: {count}
<button onClick={() => setCount(initialCount)}>Reset</button>
<button onClick={() => setCount(prevCount => prevCount - 1)}>-</button>
<button onClick={() => setCount(prevCount => prevCount + 1)}>+</button>
</>
);
}
// 旧class用在bind this和生命周期中的操作显得隆余
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {count: 1};
// 为了在回调中使用 `this`,这个绑定是必不可少的
this.handleClick = this.handleClick.bind(this);
}
handleClick(){
this.setState(state => ({
count: state.count+1
}));
}
componentDidMount() {
}
componentWillUnmount() {
}
render() {
return (
<div>
<h1 onClick={this.handleClick}>Hello, world!</h1>
</div>
);
}
}
// vue2 需要定义三次代码块实现监听属性id的变化
export default {
props:["id"]
methods:{
handleChange(){
console.log("xxx")
}
}
watch:{
id:'handleChange'
}
}
// vue3 只需要少量代码
export default defineComponent({
setup(props) {
watchEffect(()=>{
console.log(props.id)
})
},
})
export default defineComponent({
setup() {
// 侦听一个getter
const state = reactive({ count: 0 })
watch(
() => state.count,
(count, prevCount) => {
/* ... */
}
)
// 直接侦听一个ref
const count = ref(0)
watch(count, (count, prevCount) => {
/* ... */
})
},
})
function useFeatureX() {
const state = reactive({
foo: 1,
bar: 2
})
// 逻辑运行状态
// 返回时转换为ref
return toRefs(state)
}
export default {
setup() {
// 可以在不失去响应性的情况下破坏结构
const { foo, bar } = useFeatureX()
return {
foo,
bar
}
}
}
// vue2 的实现
export default {
mixins:[minxA,minB],
render() {
const {x,y} = this
// 根本不知道x和y来自哪,还有命名冲突的问题
return <>{x}{y}</>
},
}
// vue3 的实现
export default {
setup(){
const {x} = UseX()
// 这样可以解决命名冲突的问题
const {x:y} = UseY()
return{
x,
y
}
},
render() {
const {x,y} = this
// 现在清晰可知 x,y 来自哪里
return <>{x}{y}</>
},
}
// 不规范示例 代码混乱a,b,c到处混用代码解构不清晰
export default defineComponent({
setup(){
const a = ref(null)
const b = ref(null)
const changeA = ()=>{
}
const c = ref(null)
const changeB = ()=>{
}
const changeC = ()=>{
}
watchEffect(()=>{
console.log(a.value)
})
return{
dom
}
},
})
// 好的规范示例 代码清晰
/**
* @name: UseA
* @msg: a相关操作
*/
function UseA(){
const a = ref(null)
const changeA = ()=>{
}
watchEffect(()=>{
changeA()
})
return{
a
}
}
/**
* @name: UseB
* @msg: b相关操作
*/
function UseB(){
const b= ref(null)
const changeB = ()=>{
}
watchEffect(()=>{
changeB()
})
return{
b
}
}
export default defineComponent({
setup(){
// 结构清晰
const {a} = UseA()
const {b} = UseB()
return{
a,
b
}
},
})
export default defineComponent({
setup(){
const dom = ref(null)
// 必须把dom return
return{
dom
}
},
render() {
return
<>
// 定义同名ref
<div ref="dom">
<div>
</>
},
})
export default defineComponent({
render() {
// 三元运算
const comp = flag ? CompA : CompB
return <>{comp}</>
},
})
export default defineComponent({
render() {
return (
<>
<ul>
{menuList.map(item => (
<li
onClick={() => {
close(item)
}}
>
<span>{item.name}</span>
</li>
))}
</ul>
</>
)
},
})
export default defineComponent({
render() {
const style = flag? "display:block":"display:none"
return (
<div style={style}>
</div>
)
},
})
// 关键是文件后缀为 module.scss
import styles from './index.module.scss'
export default defineComponent({
render() {
return (
// 使用 styles对象去获取引用
<div style={style['yxt']}>
</div>
)
},
})
// 多个class
import classNames from 'classnames'
import styles from './index.module.scss'
export default defineComponent({
render() {
// 使用classNames 方法进行样式合并
const content = classNames(
styles['item'],
styles['item-isActive'],
)
return (
<div class ={content}>
</div>
)
},
})
// 根据变量渲染不同的class
import classNames from 'classnames'
import styles from './index.module.scss'
export default defineComponent({
render() {
//使用函数变量控制样式,在setup中也可与数据关联判断
let pptClass = ()=>{
if(flag){
return styles['item']
}else{
return styles['item-isActive']
}
}
return (
<div class={pptClass()}>
</div>
)
},
})
export default defineComponent({
render() {
// 事件或者map 循环的参数都可以传递
const handleClick = (event,arg?)=>{}
return (
<div onClick={(event) => {handleClick()}}>
</div>
)
},
})
// 区别之前的Vue.extend()方式
import { createApp } from 'vue'
import message from './Message'
// id 自增计数
let count = 0
export enum Types {
SUCCESS = 'success',
ERROR = 'error',
WARNING = 'warning',
}
// 只需要将Message进行挂载或者单独引用Message 方法进行调用
export const Message = (options: Options) => {
const div = document.createElement('div')
const id = `yxt-message-${count + 1}`
div.setAttribute('id', id)
document.body.appendChild(div)
const instance = createApp(message, {
type: options.type,
content: options.content,
id: id,
})
// 挂载到 body 下
document.body.appendChild(instance.mount(`#${id}`).$el as HTMLElement)
count++
}
// 引用组件
export default defineComponent({
render() {
const defaults = () => {
return <div>{pptNote}</div>
}
return (
<>
<div class={styles.pptDetail}>
<yxt-tabs>
<yxt-tab-pane
title="备注"
scopedSlots={defaults}
></yxt-tab-pane>
</yxt-tabs>
</div>
</>
)
},
})
// 子组件渲染
export default defineComponent({
render() {
if (this.$props.scopedSlots) {
return this.$props.scopedSlots()
} else {
return <></>
}
},
}
import { ComponentInternalInstance, getCurrentInstance } from 'vue'
/**
* @name: UseThis
* @msg: 在setup中获取this 属性, 方便使用$xx去调用
*/
export function UseThis() {
const { proxy } = getCurrentInstance() as ComponentInternalInstance
return {
proxy: proxy as any,
}
}