[Book delivery] Front-end series 16 episodes - vue3 paradigm, vue-i18n-next, watch, watchEffect

Book delivery activity: choose 1 fan classmate and send it free of charge.
Deadline: 19:00 on May 26, 2023
Participate in the comment area at the bottom
and talk : Please share your reading gains in the comments.

Notes on China-Taiwan Landing——Business Servitization and Data Assetization

34478b7e117489900fa9221ca847b722.jpeg

vue-i18n-next

To use i18n in vue3, you need to install the version [vue-i18n v9]

npm install vue-i18n@9

Create src\lang\index.ts and  createI18n create an i18n instance with:

// src\lang\index.ts
    import { createI18n } from 'vue-i18n'
    import { LANG_VALUE } from '@/common/enum'
    import ch from './ch'
    import en from './en'

    const i18n = createI18n({
      legacy: false,
      locale: getLanguage(),
      messages: {
        [LANG_VALUE.ch]: ch,
        [LANG_VALUE.En]: en
      }
    })
    export default i18n

If you are using the Composition API, you need to define it as false

  • locale : The current language to be displayed. The value is the previous user's language selection, read from the browser cache. If there is no data in the cache, by  navigator.language getting the language used by the browser:

// src\lang\index.ts
import { localCache } from '@/utils'
export function getLanguage() {
  const chooseLanguage = localCache.getItem(LANGUAGE)
  if (chooseLanguage) return chooseLanguage

  // 如果没有选择语言
  const language = navigator.language.toLowerCase()
  const locales = [LANG_VALUE.En, LANG_VALUE.Zh]
  for (const locale of locales) {
    if (language.indexOf(locale) > -1) {
      return locale
    }
  }
  return LANG_VALUE.Zh
}
// src\common\enum.ts
export enum LANG_VALUE {
  En = 'en',
  Zh = 'zh-Hans'
}
  • messages : Translation files corresponding to different languages:

// src\lang\zh-Hans.ts
export default {
  baoguochuku: '包裹出库',
  sousuo: '搜索'
}
// src\lang\en.ts
export default {
  baoguochuku: 'Outbound',
  sousuo: 'Search'
}
// src\main.ts,省略其它代码
import i18n from './lang/index'
app.use(i18n)

Import from vue-i18n  useI18n, then call to generate  i18n an instance, and then deconstruct  t the method from it

  • <template> used in 

<template>
  <n-button>
    <slot name="submitBtnText">{
    
    { $t('xxx') }}</slot>
  </n-button>
</template>

It is used directly  $t because there is a  globalInjection configuration item called by default  true, which helps us inject  $t the method globally. If it is set to  false:

// src\lang\index.ts
const i18n = createI18n({
  globalInjection: false
})

In the ts file, configure the generated  i18n instance in src\lang\index.ts

// src\service\request\index.ts
import i18n from '@/lang'
if (!axios.isCancel(error)) {
  dialog.error({
    title: i18n.global.t('biaoti'),
    // ...
  })
}
import { watch, ref } from 'vue'
 setup() {
      let mes = ref('会好的')
      //第一种情况 监听ref定义的一个响应式数据
      watch(mes, (qian, hou) => {
        console.log('变化----', qian, hou)
      }, {immediate: true})
 }

 setup() {
      let mes = ref('会好的')
      let mess = ref('我是谁')
      //第二种情况 监听ref定义的多个响应式数据
      watch([mes,mess], (qian, hou) => {
        console.log('变化----', qian, hou)
      },{immediate:true})
 }

setup() {
       let rea=reactive({
        name:'我是谁',
        obj:{
          salary:20
        }
      })
      //第三种情况 监听reactive定义的属性
       watch(rea,(qian,hou)=>{
        console.log('rea变化了',qian,hou)
      })
 }

stop listening

const stop = watchEffect(() => {
  /* ... */
})

// later
stop()
const data = ref(null)
watchEffect(async onInvalidate => {
  onInvalidate(() => {
    /* ... */
  }) // 我们在Promise解析之前注册清除函数
  data.value = await fetchData(props.id)
})
  1. watch can access new and old values, watchEffect cannot.

  2. watchEffect has side effects, which will be triggered before the DOM is mounted or updated , and we need to clear the side effects ourselves.

  3. Watch is lazy execution , that is, it will only be executed when the monitored value changes, but watchEffect is different, and watchEffect will be executed every time the code is loaded .

  4. Watch needs to specify the object to monitor, and also needs to specify the callback to monitor. watchEffect does not need to specify which attribute to monitor, which attribute is used in the monitoring callback function, and which attribute is monitored.

  • It is recommended to use vscode and install the volar plug-in to assist development

<template>
    <div>{
    
    { numberRef }}</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const numberRef = ref(0) // ===> Ref<number>
const stringRef = ref("") // ===> Ref<string>
interface IFoo {
    bar: string
}
const fooRef = ref<IFoo>() // ===> Ref<IFoo | undefined>
</script>
<template>
    <div>{
    
    { book1.bar }}</div>
</template>
<script setup lang="ts">
import { reactive } from 'vue';
interface IFoo {
    bar: string
}
// 第一种
const book1 = reactive<IFoo>({ bar: 'bar' })
// 第二种
const book2: IFoo = reactive({ bar: 'bar' })
// 第三种
const book3 = reactive({ bar: 'bar' }) as IFoo
</script>
<template>
    <div>{
    
    { fooComputed?.bar }}</div>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue';
const numberRef = ref(0) // ===> Ref<number>
const numberComputed = computed(() => numberRef.value) // ===> ComputedRef<number>
interface IFoo {
    bar: string
}
const fooRef = ref<IFoo>() // ===> Ref<IFoo | undefined>
const fooComputed = computed(() => {
    return fooRef.value
}) // ===> ComputedRef<IFoo | undefined>
</script>
<template>
    <div>{
    
    { fooComputedWritable?.bar }}</div>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue';
interface IFoo {
    bar: string
}
const fooRef = ref<IFoo>() // ===> Ref<IFoo | undefined>
const fooComputedWritable = computed({
    get: () => {
        return fooRef.value
    },
    set: (value) => {
        fooRef.value = value
    }
}) // ===> WritableComputedRef<IFoo | undefined>
</script>
<template>
    <div>{
    
    { numberRef }}</div>
</template>
<script setup lang="ts">
import { ref, watch, watchEffect } from 'vue';
const numberRef = ref(0) // ===> Ref<number>
interface IFoo {
    bar: string
}
const fooRef = ref<IFoo>() // ===> Ref<IFoo | undefined>

watchEffect(() => console.log(fooRef.value?.bar))
watch(numberRef, () => {
    console.log(`numberRef变化了`)
})
const stop = watch(fooRef, () => {
    console.log(`fooRef变化了`)
}, {
    deep: true
}) // 检查深度嵌套的对象或数组
stop(); // 停止侦听
</script>
<template>
    <div></div>
</template>
<script setup lang="ts">
import { nextTick } from 'vue';

nextTick(() => {
    // ...
})

// 还可以使用 async/await
async () => {
    await nextTick()
    // ....
}
</script>
<template>
    <Foo></Foo>
    <foo-item></foo-item>
    <component :is="Foo" />
    <component :is="someCondition ? Foo : FooItem" />
</template>
<script setup lang="ts">
import Foo from "./Foo.vue"
import FooItem from "./FooItem.vue"
const someCondition = false
</script>
<template>
    <div>
        <!-- 直接使用即可,不需要toRefs转换 -->
        {
    
    { foo }}
    </div>
</template>
<script setup lang="ts">
import { toRefs } from 'vue';
interface ICustomType {
    foo: string,
    bar: string
}
const props = defineProps({
    foo: String, // 使用构造函数声明类型
    fooMultiTypes: [String, Number], // 多个类型
    fooCustomType: Object as () => ICustomType, // 自定义类型
    fooCustomTypeWithRequire: {
        type: Object as () => ICustomType,
        required: true
    }, // 自定义类型,必选
    fooCustomTypeWithDefault: {
        type: Object as () => ICustomType,
        default: () => {
            return {
                foo: "foo",
                bar: "bar"
            }
        }
    }, // 自定义类型,带默认值
})

// 1. 可以在模板<template>中使用声明的props,不需要用toRefs转换
// 2. 如果某一个值需要在setup中使用,则需要用toRefs转换下,然后把它解构出来
const {
    foo,  // ===> Ref<string | undefined> | undefined
    fooMultiTypes, // ===> Ref<string | number | undefined> | undefined
    fooCustomType, // ===> Ref<ICustomType | undefined> | undefined
    fooCustomTypeWithRequire, // ===> Ref<ICustomType>
} = toRefs(props)
</script>
<template>
    <div>
        <!-- 直接使用即可,不需要toRefs转换 -->
        {
    
    { foo }}
    </div>
</template>
<script setup lang="ts">
import { toRefs } from 'vue';
interface ICustomType {
    foo: string,
    bar: string
}
const props = defineProps<{
    foo?: string,
    fooWithRequire: string,
    fooMultiTypes: string | number,
    fooCustomType?: ICustomType,
    fooCustomTypeWithRequire: ICustomType
}>()

// 泛型方式声明默认值,需要使用withDefaults 编译器宏
const propsWithDefault = withDefaults(
    defineProps<{
        fooCustomTypeWithDefault: ICustomType
    }>(),
    {
        fooCustomTypeWithDefault: () => {
            return {
                foo: "foo",
                bar: "bar"
            }
        }
    })

// 1. 可以在模板<template>中使用声明的props,不需要用toRefs转换
// 2. 如果某一个值需要在setup中使用,则需要用toRefs转换下,然后把它解构出来
const {
    foo,  // ===> Ref<string | undefined> | undefined
    fooWithRequire,  // ===> Ref<string>
    fooMultiTypes, // ===> Ref<string | number>
    fooCustomType, // ===> Ref<ICustomType | undefined> | undefined
    fooCustomTypeWithRequire, // ===> Ref<ICustomType>
} = toRefs(props)

const {
    fooCustomTypeWithDefault,  // ===> Ref<ICustomType>
} = toRefs(propsWithDefault)
</script>
// FooBar.vue
<template>
    <div>
        <!-- 一个单文件组件可以通过它的文件名被其自己所引用 -->
        <FooBar></FooBar>
        <foo-bar></foo-bar>

        <foo-bar-other></foo-bar-other>
    </div>
</template>
<script setup lang="ts">
// 使用 import 别名导入避免冲突
import { default as FooBarOther } from './others/FooBar.vue'
</script>
<template>
    <div></div>
</template>
<script setup lang="ts">
import {
    onBeforeMount,
    onMounted,
    onBeforeUpdate,
    onUpdated,
    onBeforeUnmount,
    onUnmounted,
    onErrorCaptured,
    onRenderTracked,
    onRenderTriggered,
    onActivated,
    onDeactivated
} from "vue"

// 直接使用就好了
onMounted(() => {
    // ...
})
</script>
<template>
    <div></div>
</template>
<script setup lang="ts">
import { provide } from 'vue';
export interface IUser {
    name: string,
    age: number
}
provide("name", "foo")
provide<IUser>("user", {
    name: "foo",
    age: 23
})
</script>
<template>
    <div></div>
</template>
<script setup lang="ts">
import { inject } from 'vue';
import { IUser } from './Foo.vue';

const name = inject<string>("name") // ===> string | undefined
const user = inject<IUser>("user") // ===> IUser | undefined
</script>
import { createApp } from 'vue';
import App from './App.vue'

createApp(App).mount('#app')

Using  the import and export syntax of import and  export , the function of packaging modules on demand is realized, and the file size of the packaged project is significantly smaller.

<template>
  <div id="app">
      <p>{
    
    { number }}</p>
      <button @click="add">增加</button>
  </div>
</template>

<script>
// 1. 从 vue 中引入 ref 函数
import {ref} from 'vue'
export default {
  name: 'App',
  setup() {
      // 2. 用 ref 函数包装一个响应式变量 number
      let number = ref(0)

      // 3. 设定一个方法
      function add() {
          // number是被ref函数包装过了的,其值保存在.value中
          number.value ++
      }

      // 4. 将 number 和 add 返回出去,供template中使用
      return {number, add}
  }
  
}
</script>

setup The function also has two parameters, respectively  props , context, the former stores the parameter names and corresponding values ​​that define the current component that allows the outside world to pass over; the latter is a context object, from which you can access  attr , emit ,slots

<template>
  <div id="app"></div>
</template>

<script>
// 1. 从 vue 中引入 多个生命周期函数
import {onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, unMounted} from 'vue'
export default {
  name: 'App',
  setup() {
      onBeforeMount(() => {
          // 在挂载前执行某些代码
      })

      onMounted(() => {
          // 在挂载后执行某些代码
      })

      onBeforeUpdate(() => {
          // 在更新前前执行某些代码
      })

      onUpdated(() => {
          // 在更新后执行某些代码
      })

      onBeforeUnmount(() => {
          // 在组件销毁前执行某些代码
      })

      unMounted(() => {
          // 在组件销毁后执行某些代码
      })

      return {}
  }
  
}
</script>

reactive The method is used to create a responsive data object, and this API also solves  defineProperty the defect of Vue2 by implementing data responsiveness

<template>
  <div id="app">
   <!-- 4. 访问响应式数据对象中的 count  -->
   {
    
    { state.count }}
  </div>
</template>

<script>
// 1. 从 vue 中导入 reactive 
import {reactive} from 'vue'
export default {
  name: 'App',
  setup() {
      // 2. 创建响应式的数据对象
      const state = reactive({count: 3})

      // 3. 将响应式数据对象state return 出去,供template使用
      return {state}
  }
}
</script>

.value It   needs to be added when accessing the wrapped object  setup in the function  , and it is not needed when accessing in the template, because it will automatically recognize whether it is  wrapped  when compiling reftemplateref

<script>
// 1. 导入 toRef
import {toRef} from 'vue'
export default {
    setup() {
        const obj = {count: 3}
        // 2. 将 obj 对象中属性count的值转化为响应式数据
        const state = toRef(obj, 'count')

        // 3. 将toRef包装过的数据对象返回供template使用
        return {state}
    }
}
</script>
<script>
// 1. 导入 ref
import {ref} from 'vue'
export default {
    setup() {
        const obj = {count: 3}
        // 2. 将 obj 对象中属性count的值转化为响应式数据
        const state = ref(obj.count)

        // 3. 将ref包装过的数据对象返回供template使用
        return {state}
    }
}
</script>
<template>
    <p>{
    
    { state1 }}</p>
    <button @click="add1">增加</button>

 <p>{
    
    { state2 }}</p>
    <button @click="add2">增加</button>
</template>

<script>
import {ref, toRef} from 'vue'
export default {
    setup() {
        const obj = {count: 3}
        const state1 = ref(obj.count)
        const state2 = toRef(obj, 'count')

        function add1() {
            state1.value ++
            console.log('原始值:', obj);
            console.log('响应式数据对象:', state1);
        }

        function add2() {
            state2.value ++
            console.log('原始值:', obj);
            console.log('响应式数据对象:', state2);
        }

        return {state1, state2, add1, add2}
    }
}
</script>

refIt is a copy  of the original data and will not affect the original value. At the same time, the view will be updated synchronously after the value of the responsive data object changes.

The view has not changed, the original value has changed, and the value of the responsive data object has also changed, which means that it is  a referencetoRef  to the original data , which will affect the original value, but the view will not be updated after the value of the responsive data object changes

  1. ref is a copy of the incoming data; toRef is a reference to the incoming data

  2. ref A change in the value of will update the view; toRef a change in the value of will not update the view

toRefs , its role is to convert the values ​​of all attributes in the incoming object into responsive data objects. This function supports one parameter, that is, the  obj object

When passed  obj as a parameter to  reactive generate a responsive data object, if there  obj are more than one level, each level will be wrapped  Proxy once

shallowReactive

a shallow reactive

<template>
 <p>{
    
    { state.a }}</p>
 <p>{
    
    { state.first.b }}</p>
 <p>{
    
    { state.first.second.c }}</p>
 <button @click="change1">改变1</button>
 <button @click="change2">改变2</button>
</template>
<script>
import {shallowReactive} from 'vue'
export default {
    setup() {
        const obj = {
            a: 1,
            first: {
                b: 2,
                second: {
                    c: 3
                }
            }
        }

        const state = shallowReactive(obj)

        function change1() {
            state.a = 7
        }

        function change2() {
            state.first.b = 8
            state.first.second.c = 9
            console.log(state);
        }

        return {state}
    }
}
</script>

Changed the second layer  b and the third layer  c, although the value has changed, but the view is not updated; when the first layer is changed  a , the entire view is updated; shallowReactive the value of the first layer attribute is monitored, once it happens changed, update the view

shallowRef

A shallow one  ref, the  shallowReactive same as the one used for performance optimization

shallowReactive It is the data change of the first layer of the monitoring object used to drive the view update, then  shallowRef it is  .value the change of the monitored value to update the view

<template>
 <p>{
    
    { state.a }}</p>
 <p>{
    
    { state.first.b }}</p>
 <p>{
    
    { state.first.second.c }}</p>
 <button @click="change1">改变1</button>
 <button @click="change2">改变2</button>
</template>

<script>
import {shallowRef} from 'vue'
export default {
    setup() {
        const obj = {
            a: 1,
            first: {
                b: 2,
                second: {
                    c: 3
                }
            }
        }

        const state = shallowRef(obj)
        console.log(state);

        function change1() {
            // 直接将state.value重新赋值
            state.value = {
                a: 7,
                first: {
                    b: 8,
                    second: {
                        c: 9
                    }
                }
            }
        }

        function change2() {
            state.value.first.b = 8
            state.value.first.second.c = 9
            console.log(state);
        }

        return {state, change1, change2}
    }
}
</script>

triggerRef , call it to update the view immediately, and it receives a parameter  , which is  the object state to be updated ref

<template>
 <p>{
    
    { state.a }}</p>
 <p>{
    
    { state.first.b }}</p>
 <p>{
    
    { state.first.second.c }}</p>
 <button @click="change">改变</button>
</template>

<script>
import {shallowRef, triggerRef} from 'vue'
export default {
    setup() {
        const obj = {
            a: 1,
            first: {
                b: 2,
                second: {
                    c: 3
                }
            }
        }

        const state = shallowRef(obj)
        console.log(state);

        function change() {
            state.value.first.b = 8
            state.value.first.second.c = 9
            // 修改值后立即驱动视图更新
            triggerRef(state)
            console.log(state);
        }

        return {state, change}
    }
}
</script>

toRaw method is used to get  ref or  reactive object's raw data

<template>
 <p>{
    
    { state.name }}</p>
 <p>{
    
    { state.age }}</p>
 <button @click="change">改变</button>
</template>

<script>
import {reactive} from 'vue'
export default {
    setup() {
        const obj = {
            name: 'xxx',
            age: 22
        }

        const state = reactive(obj) 

        function change() {
            state.age = 90
            console.log(obj); // 打印原始数据obj
            console.log(state);  // 打印 reactive对象
        }

        return {state, change}
    }
}
</script>

toRaw The method  reactive gets the original data from the object

It is very convenient to do some performance optimization by modifying the value of the original data without updating the view

<script>
import {reactive, toRaw} from 'vue'
export default {
    setup() {
        const obj = {
            name: 'xxx',
            age: 22
        }

        const state = reactive(obj) 
        const raw = toRaw(state)

        console.log(obj === raw)   // true
    }
}
</script>

markRaw The method can mark the original data as non-responsive, even if it is used  ref or  reactive packaged, it still cannot achieve data responsiveness. It receives a parameter, that is, the original data, and returns the marked data

<template>
 <p>{
    
    { state.name }}</p>
 <p>{
    
    { state.age }}</p>
 <button @click="change">改变</button>
</template>

<script>
import {reactive, markRaw} from 'vue'
export default {
    setup() {
        const obj = {
            name: 'xxx',
            age: 22
        }
        // 通过markRaw标记原始数据obj, 使其数据更新不再被追踪
        const raw = markRaw(obj)   
        // 试图用reactive包装raw, 使其变成响应式数据
        const state = reactive(raw) 

        function change() {
            state.age = 90
            console.log(state);
        }

        return {state, change}
    }
}
</script>

markRaw Whether the data processed  by  the method can be reactive packaged into responsive data

watch Both  watchEffect are used to monitor a data change to perform a specified operation

<script>
import {ref, watch} from 'vue'
export default {
    setup() { 
        const state = ref(0)

        watch(state, (newValue, oldValue) => {
            console.log(`原值为${oldValue}`)
            console.log(`新值为${newValue}`)
            /* 1秒后打印结果:
                            原值为0
                            新值为1
            */
        })

        // 1秒后将state值+1
        setTimeout(() => {
            state.value ++
        }, 1000)
    }
}
</script>
<script>
import {reactive, watch} from 'vue'
export default {
    setup() { 
        const state = reactive({count: 0})

        watch(() => state.count, (newValue, oldValue) => {
            console.log(`原值为${oldValue}`)
            console.log(`新值为${newValue}`)
            /* 1秒后打印结果:
                            原值为0
                            新值为1
            */
        })

        // 1秒后将state.count的值+1
        setTimeout(() => {
            state.count ++
        }, 1000)
    }
}
</script>
<script>
import {reactive, watch} from 'vue'
export default {
    setup() { 
        const state = reactive({ count: 0, name: 'xx' })

        watch(
            [() => state.count, () => state.name], 
            ([newCount, newName], [oldvCount, oldvName]) => {
                console.log(oldvCount) // 旧的 count 值
                console.log(newCount) // 新的 count 值
                console.log(oldName) // 旧的 name 值
                console.log(newvName) // 新的 name 值
            }
        )

        setTimeout(() => {
          state.count ++
          state.name = 'dd'
        }, 1000)
    }
}
</script>

The watch method will return a stop method. If you want to stop monitoring, you can directly execute the stop function

watchEffect

  1. No need to manually import dependencies

  2. A callback function will be executed every time it is initialized to automatically obtain dependencies

  3. The original value cannot be obtained, only the changed value can be obtained

<script>
import {reactive, watchEffect} from 'vue'
export default {
    setup() { 
        const state = reactive({ count: 0, name: 'xx' })

        watchEffect(() => {
            console.log(state.count)
            console.log(state.name)
            /*  初始化时打印:
                            0
                            xx

                1秒后打印:
                            1
                            da
            */
        })

        setTimeout(() => {
          state.count ++
          state.name = 'da'
        }, 1000)
    }
}
</script>

const store = useStore()

Vue3 getCurrentInstance().ctx

<template>
  <div>
    <div ref="el">div元素</div>
  </div>
</template>

<script>
import { ref, onMounted } from 'vue'
export default {
  setup() {
      // 创建一个DOM引用,名称必须与元素的ref属性名相同
      const el = ref(null)

      // 在挂载后才能通过 el 获取到目标元素
      onMounted(() => {
        el.value.innerHTML = '内容被修改'
      })

      // 把创建的引用 return 出去
      return {el}
  }
}
</script>

Add the group to contact the author vx: xiaoda0423

Warehouse address: https://github.com/webVueBlog/WebGuideInterview

Guess you like

Origin blog.csdn.net/qq_36232611/article/details/130858350