【書籍納品】フロントエンドシリーズ14話~Vue3~セットアップ

本配信活動:ファンの同級生無料でお送りします。
締切:2023年5月18日19時
下部のコメント欄に参加して
トーク:読書の成果をコメントで共有してください。

フロントエンドエンジニアリング: Vue.js 3.0に基づいた設計と実践

f55af1f836ff577901e4c36fe12ba380.jpeg

ページビュー(ページビュー、PV)と訪問者(ユニーク訪問者、UV)

// index.vue
<template>
  <a-button @click="onClick">查询</a-button>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator'

@Component({})
export default class Page extends Vue {
  // PV 埋点
  mounted() {
    window.DATracker.track('page_id', {})
  }

  // 交互埋点
  onClick() {
    window.DATracker.track('event_id', {})
  }
}
</script>

ページ操作の埋め込みポイントについては、 trackEvent メソッドを統一された方法でカプセル化できます。

// utils/track.ts

/**
 * 日志上报
 * @param id 事件id
 * @param data 上报数据
 */
export const trackEvent = (id, data = {}) => {
  window.DATracker.track(id, data)
}

クリック イベントの処理は比較的単純で、クリックするたびにデータ レポートがトリガーされます。

// src/directives/track/click.js
import { sendUBT } from "../../utils"

export default class Click {
  add(entry) {
    // console.log("entry", entry);
    const traceVal = entry.el.attributes["track-params"].value
    const traceKey = entry.el.attributes["trace-key"].value
    const { clickAction, detail } = JSON.parse(traceVal)
    const data = {
      action: clickAction,
      detail,
    }
    entry.el.addEventListener("click", function() {
      console.log("上报点击埋点", JSON.parse(traceVal))
      console.log("埋点key", traceKey)
      sendUBT(traceKey, data)
    })
  }
}
  • v-track:click|exposure

  • v-track:exposure

自動化の形で、これらの埋もれたポイントを追加するにはどうすればよいでしょうか?

8785d5cf8a7eeedc95fe7aafeb6d2ff86.png
画像.png
SysLoginController
SysLoginService
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * @author
 *
 * 跨域访问配置
 */
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("*")
                .allowCredentials(true)
                .allowedMethods("GET", "POST", "DELETE", "PUT")
                .maxAge(3600);
    }

}
server: {
        // host: '0.0.0.0',
        // 反向代理解决跨域
        proxy: {
            [env.VITE_APP_PORT]: {
                target: 'htts://',
                changeOrigin: true,
                rewrite: (path) => path.replace(new RegExp('^' + env.VITE_APP_PORT), '')
            }
        }
        // open: true, // 运行是否自动打开浏览器
        // port: Number(env.VITE_APP_PORT),
    },
// vue.config.js

const env = process.env;

module.exports = {
  devServer: {
    proxy: {
      [env.VITE_APP_PORT]: {
        target: 'htts://example.com',
        changeOrigin: true,
        rewrite: (path) => path.replace(new RegExp('^' + env.VITE_APP_PORT), '')
      }
    },
    open: true, // 运行是否自动打开浏览器
    port: Number(env.VITE_APP_PORT),
  },
};
89f16d3062a4262176013d3f453ace0c.png
画像.png
37c273ccc6bb0a78badddc4be5cf59f8.png
画像.png
server: {
        // host: '0.0.0.0',
        // 反向代理解决跨域
        proxy: {
            '/api': {
                target: 'http://xxx:1606',
                changeOrigin: true,
                secure: false,
                rewrite: (path) => path.replace(/^\/api/, '')
            }
        }
        // open: true, // 运行是否自动打开浏览器
        // port: Number(env.VITE_APP_PORT),
    },
css: {
        // CSS 预处理器
        preprocessorOptions: {
            //define global scss variable
            scss: {
                javascriptEnabled: true,
                additionalData: `@use "@/styles/variables.scss" as *;`
            }
        }
    },

CSSコードの前処理を行うための設定項目です。この構成では、使用するプリプロセッサとその構成オプションを指定できます。その中でもscss人気の高いのが CSS プリプロセッサです。javascriptEnabled: trueJavaScript 構文が SCSS で使用できることを示します。additionalData属性を使用すると、すべての SCSS ファイルに含まれるグローバル SCSS 変数を定義できます。この例では、@useディレクティブはvariables.scssファイルをインポートし、そのすべての内容をグローバル変数として使用できるようにします。

Vue.jstoRawメソッドは、リアクティブ オブジェクトをディープトラバースし、その非リアクティブなプリミティブ値を返すメソッドです。reactiveこれを使用して、refおよびVue 3 でcomputed作成されたリアクティブ オブジェクトの生データにアクセスできます。

Vue.js では、応答性の高いオブジェクトを操作するときに、オブジェクトのプロパティの変更を追跡し、必要に応じてビューを更新するために、それをプロキシ オブジェクト内にラップします。ただし、プロキシ オブジェクトではなく、オブジェクトの元の値に直接アクセスする必要がある場合があります。その後、toRawメソッドを使用して元の値を取得できます。

例えば:

import { reactive, toRaw } from 'vue'

const state = reactive({ count: 0 })

console.log(state.count) // 0
console.log(toRaw(state).count) // 0

state.count++

console.log(state.count) // 1
console.log(toRaw(state).count) // 0

上の例では、 counter を含むリアクティブ オブジェクトを作成しましたstatestate.count次に、 と をそれぞれ使用してカウンタの値にアクセスしますtoRaw(state).countカウンタをインクリメントすると、state.countの値は 1 に更新されますが、toRaw(state).count反応性システムによって管理されないため、初期値 0 のままになります。

このコードでは、関数paramsを使用して変数をコピーしtoRawObject.assign. を使用して新しいオブジェクトに割り当てます。結果として得られるオブジェクトは、の浅いコピーparamsInitです。params

使用の目的toRawは、対応するプロパティを持たないオブジェクトのコピーを作成することです。つまり、paramsが応答性の高いオブジェクト (Vue.js の関数を使用して作成されたオブジェクトなどreactive) の場合、結果として得られるparamsInitオブジェクトは応答性がありません。

JavaScript では、Object.assign({}, obj)オブジェクトの浅いコピーを作成する一般的な方法です。新しい空のオブジェクトを作成し、objソース オブジェクト ( ) の列挙可能なすべての独自のプロパティを宛先オブジェクト (空のオブジェクト) にコピーします。Object.assign最初のパラメータの は、{}ターゲット オブジェクトとして使用される新しい空のオブジェクトを作成していることを示します。

全体として、このコードは のparams非応答コピーを作成し、それを新しい変数に割り当てますparamsInit

events
size-change page-size 改变时触发
current-change current-page 改变时触发
prev-click 用户点击上一页按钮改变当前页时触发
next-click 用户点击下一页按钮改变当前页时触发
background 是否为分页按钮添加背景色 
page-size / v-model:page-size 每页显示条目个数
default-page-size 每页显示条目数的初始值
total 总条目数
page-count 总页数, total 和 page-count 设置任意一个就可以达到显示页码的功能;如果要支持 page-sizes 的更改,则需要使用 total 属性
pager-count 设置最大页码按钮数。 页码按钮的数量,当总页数超过该值时会折叠
current-page / v-model:current-page 当前页数
default-current-page 当前页数的初始值
layout 组件布局,子组件名用逗号分隔
page-sizes 每页显示个数选择器的选项设置
popper-class 每页显示个数选择器的下拉框类名
prev-text 替代图标显示的上一页文字
prev-icon 上一页的图标, 比 prev-text 优先级更高
next-text 替代图标显示的下一页文字
next-icon 下一页的图标, 比 next-text 优先级更高
disabled 是否禁用分页
hide-on-single-page 只有一页时是否隐藏
warning
total 和 page-count 必须传一个,不然组件无法判断总页数;优先使用 page-count;
如果传入了 current-page,必须监听 current-page 变更的事件(@update:current-page),否则分页切换不起作用;
如果传入了 page-size,且布局包含 page-size 选择器(即 layout 包含 sizes),必须监听 page-size 变更的事件(@update:page-size),否则分页大小的变化将不起作用。

現在のページとページ サイズの変更を監視する場合は、v-model 双方向バインディングを使用することをお勧めします。

という名前の定数を定義しますpager

computed()関数は、計算されたプロパティを作成するために使用されます。この例では、computed()オブジェクトを関数に渡すことによって計算プロパティが作成されます。このオブジェクトにはgetと の2 つのメソッドがありますset

get()このメソッドはprops.modelValue、コンポーネントのmodelValueプロパティである を、計算されたプロパティの値として返します。

set()メソッドは 1 つのパラメーターを受け取りvalue、呼び出されるときにemit()メソッドを使用してイベントを起動し、modelValueプロパティを更新します。イベント名は であり'update:modelValue'defineEmits()関数によって定義されます。

計算されたプロパティの値が変更されると、set()メソッドが呼び出され、プロパティを'update:modelValue'更新するためにイベントがトリガーされるため、コンポーネントのテンプレート内の計算されたプロパティにバインドされた要素は自動的に更新に応答します。modelValuepager

という名前の定数を定義しますemit

defineEmits()関数は、コンポーネントが発行できるイベントをタイプセーフに定義するために使用されます。この例では、と のdefineEmits()2 つのイベントが定義されており、それぞれコンポーネント トリガーイベントとプロパティ更新イベントを表します。'change''update:modelValue'changemodelValue

defineEmits()この関数は、定義されたすべてのイベントとそのパラメーター タイプを指定する汎用パラメーターを受け入れます。この例では、ジェネリック パラメーターは、changeイベントにはパラメーターがなく、update:modelValueイベントにはvaluetype のパラメーターがあることを指定していますany

したがって、コンポーネントがemit()これらのイベントをトリガーするメソッドを使用する場合、TypeScript または Vue.js はイベント名とパラメーターの型を検証するため、コードの信頼性と保守性が向上します。

という名前の定数は、withDefaults()および関数によって定義されますdefineProps()props

defineProps()この関数は、Vue.js コンポーネントのプロパティ (つまり、props) を表すPropsタイプの新しいオブジェクトを作成します。Props

withDefaults()この関数は 2 つの引数を受け入れます。デフォルトを組み込むオブジェクトと、デフォルトを含む 2 番目のオブジェクトです。この場合、withDefaults()デフォルトのmodelValueプロパティ、デフォルトのpageSizes配列、およびデフォルトの文字列がオブジェクトlayoutにマージされます。props

modelValueのデフォルト値は、空の オブジェクト を返す関数です{}。つまり、プロパティが に明示的に渡されない場合modelValue、デフォルトで空のオブジェクトになります。

繰り返しますが、pageSizesのデフォルト値は[20, 50, 100, 150]配列を返す関数です。これは、プロパティが明示的に渡されない場合pageSizes、デフォルトで配列になることを意味します[20, 50, 100, 150]

最後に、layoutのデフォルト値は、'total, sizes, prev, pager, next, jumper'ページング UI を構成するコンポーネントのカンマ区切りリストである文字列です。

usePaging ページネーション

ページネーション

withDefaultsは、オプション オブジェクトをマージするための Vue 3 のユーティリティ関数です。デフォルトのオプション オブジェクトと新しいオプション オブジェクトを受け取り、それらを新しいオプション オブジェクトにマージできます。

サンプルコード:

import { withDefaults } from 'vue';

const defaultOptions = {
  color: 'blue',
  fontSize: 14,
};

const options = withDefaults(defaultOptions, {
  color: 'red',
});

console.log(options); // 输出 { color: 'red', fontSize: 14 }

上の例では、defaultOptionsデフォルト オプションとして名前を付けたオブジェクトを定義しました。withDefaults次に、関数を呼び出して、それを最初の引数として渡し、新しいオプション オブジェクトを 2 番目の引数として渡します。新しいオプション オブジェクトにはデフォルト オプション オブジェクトと同じプロパティがあるためcolor、デフォルト オプション オブジェクトのプロパティ値をオーバーライドします。ただし、新しいオプション オブジェクトにはプロパティが定義されていないためfontSize、そのプロパティはデフォルトのオプション オブジェクトから取得されます。console.logコード例の出力結果に示されているように、最終的なオプション オブジェクトにはこれら 2 つのプロパティが含まれます。

d700efe55b226eb5e2f77d1637772dd1.png
画像.png

Vue3 の国際化

npm i vue-i18n --save

npm i vue-i18n@next

たとえば、中国語を保存するために新しい zh-CN.js を作成します。

const Settings = {
  settings: '设置',
  search: '搜索',
  airplaneMode: '飞行模式',
  wlan: '无线局域网',
  bluetooth: '蓝牙'
}

const Maps = {
  searchPlaceHolder: '搜索地点或地址'
}
// 按功能模块导出
export default {
  Settings,
  Maps
}

英語を保存するために新しい en-US.js を作成します。たとえば、

const Settings = {
  settings: 'Settings',
  search: 'Search',
  airplaneMode: 'Airplane Mode',
  wlan: 'WLAN',
  bluetooth: 'Bluetooth',
}

const Maps = {
  searchPlaceHolder: 'search'
}

export default {
  Settings,
  Maps
}
// 国际化多语言
import i18n from './locales/index'
app.use(i18n).mount('#app')

<div class="page-title">{
    
    { $t("Settings.settings") }}</div>

<template>
  <button @click="changeLanguage">change language</button>
</template>

<script lang="ts">
import { defineComponent  } from "vue";
import { useI18n } from "vue-i18n";

export default defineComponent({
  name: "App",
  components: {},
  setup() {
    const { locale } = useI18n({ useScope: "global" });
    function changeLanguage() {
      locale.value = "en-US"; // 切换成英文
    }
    return {
      changeLanguage,
    };
  },
});
</script>

セットアップ構文シュガーを使用する

<script setup>
これは、宣言された変数、関数、インポートによって導入されたコンテンツを公開するために Vue3.0 でのセットアップが面倒であるという問題を解決する、 単一ファイル コンポーネント (SFC) の結合 API を使用したコンパイル時の糖衣構文です。使用前にリターンを介して外部に出力されます<template/>

<script setup>
//import引入的内容
import { getToday } from './utils'  
// 变量
const msg = 'Hello!'
// 函数
function log() {
  console.log(msg)
}
</script>
//在template中直接使用声明的变量、函数以及import引入的内容
<template>
  <div @click="log">{
    
    { msg }}</div>
   <p>{
    
    {getToday()}}</p>
</template>

<script setup>シンタックスシュガー内のコードは setup() コンポーネント関数の内容にコンパイルされ、宣言された変数、関数、インポートからリターンで導入された内容を公開せずに使用でき、記述する必要はありませ<template/>export default{}

<script setup>糖衣構文内のコードは、 setup() コンポーネント関数のコンテンツにコンパイルされます。これは、 <script> コンポーネントが最初に導入されたときに 1 回だけ実行されるのではなく、コンポーネント インスタンスが作成されるたびに<script setup> コードが実行されることを意味します。

<script>
  console.log('script');//多次实例组件,只触发一次
  export default {
      setup() {
          console.log('setupFn');//每次实例化组件都触发和script-setup标签一样
      }
  }
  </script>

script-setup タグは最終的にsetup()関数のコンテンツにコンパイルされ、コンポーネントがインスタンス化されるたびに、setup 関数が 1 回インスタンス化されます。scriptタグ内のsetup関数も同様で、コンポーネントをインスタンス化するたびに一度setup関数をインスタンス化しますが、scriptタグのsetupはエクスポートデフォルトの{}内に記述する必要があり、外部は実行時に一度だけ実行されます。それは初めて紹介されます。

<script setup>インポートされたコンポーネントは自動的に登録されます

<script setup>
import MyComponent from './MyComponent.vue'
//components:{MyComponent}  不需要注册直接使用
</script>
<template>
  <MyComponent />
</template>

コンポーネント通信:  props と Emits の代わりに API を<script setup>使用する必要があります defineProps defineEmits

definePropsdefineEmits完全な型推論があり、  直接 利用できます<script setup> 。

props の代わりにdefineProps は、親コンポーネントによって渡されたデータを受け取ります (親コンポーネントはパラメータを子コンポーネントに渡します)。

<template>
  <div>父组件</div>
  <Child :title="msg" />
</template>
<script setup>
import {ref} from 'vue'
import Child from './child.vue'
const msg = ref('父的值')  //自动返回,在template直接解套使用
</script>

<template/> 親コンポーネントによって渡された props は、直接使用できます。

<script-setup> props.xx を通じて親コンポーネントから渡された props を取得する必要があります

<template>
  <div>子组件</div>
  <div>父组件传递的值:{
    
    {title}}</div>
</template>
<script setup>
//import {defineProps} from 'vue'   不需要引入
//语法糖必须使用defineProps替代props
const  props = defineProps({
  title: {
    type: String
  }
});
//script-setup 需要通过props.xx获取父组件传递过来的props
console.log(props.title) //父的值
</script>

子コンポーネントはデータを親コンポーネントに渡します (子コンポーネントはデータを外部に公開します)。

<template>
  <div>子组件</div>
  <button @click="toEmits">子组件向外暴露数据</button>
</template>
<script setup>
import {ref} from 'vue'
const name = ref('我是子组件')
//1、暴露内部数据
const  emits = defineEmits(['childFn']);
const  toEmits = () => {
  //2、触发父组件中暴露的childFn方法并携带数据
  emits('childFn',name)
}
</script>

父组件代码:

<template>
  <div>父组件</div>
  <Child  @childFn='childFn' />
  <p>接收子组件传递的数据{
    
    {childData}} </p>
</template>
<script setup>
import {ref} from 'vue'
import Child from './child.vue'
    
const childData = ref(null)    
const childFn=(e)=>{
    console.log('子组件触发了父组件childFn,并传递了参数e')
    childData=e.value
}    
       
</script>

<pagination v-model="pager" @change="getLists" />

<script setup>子コンポーネントのプロパティを親コンポーネントにアクティブに公開する必要があります:defineExpose

使用する コンポーネントについては<script setup> 、親コンポーネントは ref を渡したり、 $parent 子コンポーネントの ref などの応答データを取得したりすることができないため、defineExpose を通じてアクティブに公開する必要があります。

<script setup>
import { ref } from 'vue'
const a = 1
const b = ref(2)
//主动暴露组件属性
defineExpose({
  a,
  b
})
</script>

父组件代码:

<template>
  <div>父组件</div>
  <Child  ref='childRef' />
  <button @click='getChildData'>通过ref获取子组件的属性 </button>
</template>
<script setup>
import {ref} from 'vue'
import Child from './child.vue'
const childRef= ref()  //注册响应数据  
const getChildData =()=>{
  //子组件接收暴露出来得值
  console.log(childRef.value.a) //1
  console.log(childRef.value.b) //2  响应式数据
}    
</script>

useSlots そして useAttrs (ほとんどの人は SFC モードで開発しているため、ラベルを介してスロットをレンダリングできるため、 less を使用します)<template/><slot/>

script-setupを使用する必要がある場合は、と を置き換える必要がありますslotsattrsuseSlotsuseAttrs

インポートする必要があります:import { useSlots ,useAttrs } form 'vue'

および( attrs は props ではない親コンポーネントのサブコンポーネントに渡されるパラメータ/メソッドを取得するために使用されます。attrs は、props ではないサブコンポーネントに渡されるパラメータ/メソッドを取得するために使用されます) を介してアクセスするとより便利です<template/>親コンポーネントの props ではありません。attrs は、親コンポーネントの子コンポーネントに渡される非 props パラメータ/メソッドを取得するために使用されます。スロットは、親コンポーネントのスロットによって渡される仮想 dom オブジェクトを取得できます。これは役に立たないはずです。 SFC モードでは、JSX/TSX でよく使用されます)$slots$attrs

親コンポーネント:

<template>
  <Child msg="非porps传值子组件用attrs接收" >
    <!-- 匿名插槽 -->
    <span >默认插槽</span>
    <!-- 具名插槽 -->
    <template #title>
      <h1>具名插槽</h1>
    </template>
    <!-- 作用域插槽 -->
    <template #footer="{ scope }">
      <footer>作用域插槽——姓名:{
    
    { scope.name }},年龄{
    
    { scope.age }}</footer>
    </template>
  </Child>
</template>
<script setup>
// 引入子组件
import Child from './child.vue'
</script>

サブアセンブリ:

<template>
  <!-- 匿名插槽 -->
  <slot />
  <!-- 具名插槽 -->
  <slot name="title" />
  <!-- 作用域插槽 -->
  <slot name="footer" :scope="state" />
  <!-- $attrs 用来获取父组件中非props的传递到子组件的参数 -->
  <p>{
    
    { attrs.msg == $attrs.msg }}</p>
  <!--true  没想到有啥作用... -->
  <p>{
    
    { slots == $slots }}</p>
</template>
<script setup>
import { useSlots, useAttrs, reactive, toRef } from 'vue'
const state = reactive({
  name: '张三',
  age: '18'
})
const slots = useSlots()
console.log(slots.default()); //获取到默认插槽的虚拟dom对象
console.log(slots.title());   //获取到具名title插槽的虚拟dom对象
// console.log(slots.footer()); //报错  不知道为啥有插槽作用域的无法获取
//useAttrs() 用来获取父组件传递的过来的属性数据的(也就是非 props 的属性值)。
const attrs = useAttrs()
</script>

useSlots は、親コンポーネントによって渡されたスロットの仮想 dom オブジェクトを取得でき、これを使用してスロットのコンテンツをレンダリングできます。

<script lang='jsx'>
import { defineComponent, useSlots } from "vue";
export default defineComponent({
  setup() {
    // 获取插槽数据
    const slots = useSlots();
    // 渲染组件
    return () => (
      <div>
        {slots.default?slots.default():''}
        {slots.title?slots.title():''}
      </div>
    );
  },
});
</script>

ルーティング インスタンスのコンポーネント情報へのアクセス: ルートとルーター

setupまたは にthis直接アクセスできなくなりました(getCurrentInstance はこれを置き換えることができますが、推奨されません)this.$routerthis.$route

推奨事項: andの代わりにuseRoute関数と関数を使用してください。useRouterthis.$routethis.$router

<script setup>
import { useRouter, useRoute } from 'vue-router'
    const route = useRoute()
    const router = useRouter()
    
    function pushWithQuery(query) {
      router.push({
        name: 'search',
        query: {
          ...route.query,
        },
      })
    }
  <script/>
import router from './router'
router.beforeEach((to,from,next)=>{

})

組み合わせたAPIのナビゲーションガードを使用することもできますonBeforeRouteLeave, onBeforeRouteUpdate

<script setup>
import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'

    // 与 beforeRouteLeave 相同,无法访问 `this`
    onBeforeRouteLeave((to, from) => {
      const answer = window.confirm(
        'Do you really want to leave? you have unsaved changes!'
      )
      // 取消导航并停留在同一页面上
      if (!answer) return false
    })

    const userData = ref()

    // 与 beforeRouteUpdate 相同,无法访问 `this`
    onBeforeRouteUpdate(async (to, from) => {
      //仅当 id 更改时才获取用户,例如仅 query 或 hash 值已更改
      if (to.params.id !== from.params.id) {
        userData.value = await fetchUser(to.params.id)
      }
    })
 <script/>

複合 API ガードは、 によってレンダリングされる任意のコンポーネントでも使用でき<router-view>、コンポーネント内ガードのようにルーティング コンポーネントで直接使用する必要はありません。

<template>
  <div class="watch-test">
    <div>ref定义数组:{
    
    {arrayRef}}</div>
    <div>reactive定义数组:{
    
    {arrayReactive}}</div>
  </div>
  <div>
    <button @click="changeArrayRef">改变ref定义数组第一项</button>
    <button @click="changeArrayReactive">改变reactive定义数组第一项</button>
  </div>
</template>

<script>
  import {ref, reactive, watch} from 'vue'

  export default {
    name: 'WatchTest',
    setup() {
      const arrayRef = ref([1, 2, 3, 4])
      const arrayReactive = reactive([1, 2, 3, 4])

      //ref not deep
      const arrayRefWatch = watch(arrayRef, (newValue, oldValue) => {
        console.log('newArrayRefWatch', newValue, 'oldArrayRefWatch', oldValue)
      })

      //ref deep
      const arrayRefDeepWatch = watch(arrayRef, (newValue, oldValue) => {
        console.log('newArrayRefDeepWatch', newValue, 'oldArrayRefDeepWatch', oldValue)
      }, {deep: true})

      //reactive,源不是函数
      const arrayReactiveWatch = watch(arrayReactive, (newValue, oldValue) => {
        console.log('newArrayReactiveWatch', newValue, 'oldArrayReactiveWatch', oldValue)
      })

      // 数组监听的最佳实践- reactive且源采用函数式返回,返回拷贝后的数据
      const arrayReactiveFuncWatch = watch(() => [...arrayReactive], (newValue, oldValue) => {
        console.log('newArrayReactiveFuncWatch', newValue, 'oldArrayReactiveFuncWatch', oldValue)
      })

      const changeArrayRef = () => {
        arrayRef.value[0] = 6
      }
      const changeArrayReactive = () => {
        arrayReactive[0] = 6
      }
      return {
        arrayRef,
        arrayReactive,
        changeArrayRef,
        changeArrayReactive
      }
    }
  }
</script>

国際化オートツール

vue プロジェクトは [vue-i18n] を使用し、react プロジェクトは [react-i18next] を使用します。これらのプラグインの原理は、フロント エンド (en-US など) によって提供される言語パックの json ファイルのセットに基づいています。 json、zh-CN.json) 、各言語パックは1 対 1 に対応するデータをオブジェクトの形式でkey:value保存します。代码标记:实际渲染的语言文案

3b8d441f6b8e0ce65056a2385c68f1a9.png
画像.png
bac66e5a4f2cf4012fe968ddb489ab56.png
画像.png

フロントエンド開発では、補助プラグイン [@mango-scripts/i18n-scripts] を使用して、言語パックをワンクリックでダウンロードし、ビジネス システムにインポートします本地代码

521fbc9215eb1711294cd126c2808a30.png
画像.png
980422e711eb4cf2309e1e05d7875170.png
画像.png

本地代码言語パックをワンクリックでダウンロードしてビジネスシステムにインポートするための補助プラグイン [@mango-scripts/i18n-scripts] の使用をサポートします。

コピーライティング パーサー: [@mango-scripts/i18n-utils] babel、vue-template-compiler、hyntax、pug、svelte/compiler などに基づくターゲット ソース コードの AST 分析。

補助プラグイン: コマンダー、fs-extra、glob、inquirer などに基づく [@mango-scripts/i18n-scripts]

https://github.com/AlbertLin0923/mango-i18n-system

https://github.com/AlbertLin0923/mango-scripts/tree/main/packages/i18n-scripts

Vue3の8種類とVue2のコンポーネント12種類が通信

// Parent.vue 传送
<child :msg1="msg1" :msg2="msg2"></child>
<script>
import child from "./child.vue"
import { ref, reactive } from "vue"
export default {
    data(){
        return {
            msg1:"这是传级子组件的信息1"
        }
    },
    setup(){
        // 创建一个响应式数据
        
        // 写法一 适用于基础类型  ref 还有其他用处,下面章节有介绍
        const msg2 = ref("这是传级子组件的信息2")
        
        // 写法二 适用于复杂类型,如数组、对象
        const msg2 = reactive(["这是传级子组件的信息2"])
        
        return {
            msg2
        }
    }
}
</script>

// Child.vue 接收
<script>
export default {
  props: ["msg1", "msg2"],// 如果这行不写,下面就接收不到
  setup(props) {
    console.log(props) // { msg1:"这是传给子组件的信息1", msg2:"这是传给子组件的信息2" }
  },
}
</script>
// Parent.vue 传送
<child :msg2="msg2"></child>
<script setup>
    import child from "./child.vue"
    import { ref, reactive } from "vue"
    const msg2 = ref("这是传给子组件的信息2")
    // 或者复杂类型
    const msg2 = reactive(["这是传级子组件的信息2"])
</script>

// Child.vue 接收
<script setup>
    // 不需要引入 直接使用
    // import { defineProps } from "vue"
    const props = defineProps({
        // 写法一
        msg2: String
        // 写法二
        msg2:{
            type:String,
            default:""
        }
    })
    console.log(props) // { msg2:"这是传级子组件的信息2" }
</script>
// Child.vue 派发
<template>
    // 写法一
    <button @click="emit('myClick')">按钮</buttom>
    // 写法二
    <button @click="handleClick">按钮</buttom>
</template>
<script setup>
    
    // 方法一 适用于Vue3.2版本 不需要引入
    // import { defineEmits } from "vue"
    // 对应写法一
    const emit = defineEmits(["myClick","myClick2"])
    // 对应写法二
    const handleClick = ()=>{
        emit("myClick", "这是发送给父组件的信息")
    }
    
    // 方法二 不适用于 Vue3.2版本,该版本 useContext()已废弃
    import { useContext } from "vue"
    const { emit } = useContext()
    const handleClick = ()=>{
        emit("myClick", "这是发送给父组件的信息")
    }
</script>

// Parent.vue 响应
<template>
    <child @myClick="onMyClick"></child>
</template>
<script setup>
    import child from "./child.vue"
    const onMyClick = (msg) => {
        console.log(msg) // 这是父组件收到的信息
    }
</script>
// Child.vue
<script setup>
    // 方法一 不适用于Vue3.2版本,该版本 useContext()已废弃
    import { useContext } from "vue"
    const ctx = useContext()
    // 对外暴露属性方法等都可以
    ctx.expose({
        childName: "这是子组件的属性",
        someMethod(){
            console.log("这是子组件的方法")
        }
    })
    
    // 方法二 适用于Vue3.2版本, 不需要引入
    // import { defineExpose } from "vue"
    defineExpose({
        childName: "这是子组件的属性",
        someMethod(){
            console.log("这是子组件的方法")
        }
    })
</script>

// Parent.vue  注意 ref="comp"
<template>
    <child ref="comp"></child>
    <button @click="handlerClick">按钮</button>
</template>
<script setup>
    import child from "./child.vue"
    import { ref } from "vue"
    const comp = ref(null)
    const handlerClick = () => {
        console.log(comp.value.childName) // 获取子组件对外暴露的属性
        comp.value.someMethod() // 调用子组件对外暴露的方法
    }
</script>
// Parent.vue 传送
<child :msg1="msg1" :msg2="msg2" title="3333"></child>
<script setup>
    import child from "./child.vue"
    import { ref, reactive } from "vue"
    const msg1 = ref("1111")
    const msg2 = ref("2222")
</script>

// Child.vue 接收
<script setup>
    import { defineProps, useContext, useAttrs } from "vue"
    // 3.2版本不需要引入 defineProps,直接用
    const props = defineProps({
        msg1: String
    })
    // 方法一 不适用于 Vue3.2版本,该版本 useContext()已废弃
    const ctx = useContext()
    // 如果没有用 props 接收 msg1 的话就是 { msg1: "1111", msg2:"2222", title: "3333" }
    console.log(ctx.attrs) // { msg2:"2222", title: "3333" }
    
    // 方法二 适用于 Vue3.2版本
    const attrs = useAttrs()
    console.log(attrs) // { msg2:"2222", title: "3333" }
</script>
// Parent.vue
<child v-model:key="key" v-model:value="value"></child>
<script setup>
    import child from "./child.vue"
    import { ref, reactive } from "vue"
    const key = ref("1111")
    const value = ref("2222")
</script>

// Child.vue
<template>
    <button @click="handlerClick">按钮</button>
</template>
<script setup>
    
    // 方法一  不适用于 Vue3.2版本,该版本 useContext()已废弃
    import { useContext } from "vue"
    const { emit } = useContext()
    
    // 方法二 适用于 Vue3.2版本,不需要引入
    // import { defineEmits } from "vue"
    const emit = defineEmits(["key","value"])
    
    // 用法
    const handlerClick = () => {
        emit("update:key", "新的key")
        emit("update:value", "新的value")
    }
</script>
// Parent.vue
<script setup>
    import { provide } from "vue"
    provide("name", "沐华")
</script>

// Child.vue
<script setup>
    import { inject } from "vue"
    const name = inject("name")
    console.log(name) // 沐华
</script>
// store/index.js
import { createStore } from "vuex"
export default createStore({
    state:{ count: 1 },
    getters:{
        getCount: state => state.count
    },
    mutations:{
        add(state){
            state.count++
        }
    }
})

// main.js
import { createApp } from "vue"
import App from "./App.vue"
import store from "./store"
createApp(App).use(store).mount("#app")

// Page.vue
// 方法一 直接使用
<template>
    <div>{
    
    { $store.state.count }}</div>
    <button @click="$store.commit('add')">按钮</button>
</template>

// 方法二 获取
<script setup>
    import { useStore, computed } from "vuex"
    const store = useStore()
    console.log(store.state.count) // 1

    const count = computed(()=>store.state.count) // 响应式,会随着vuex数据改变而改变
    console.log(count) // 1 
</script>

EventBus のコンポーネント間通信

Vue3 には EventBus のコンポーネント間通信はありません

最初にインストールする npm i mitt -S

mitt.js
import mitt from 'mitt'
const mitt = mitt()
export default mitt
// 组件 A
<script setup>
import mitt from './mitt'
const handleClick = () => {
    mitt.emit('handleChange')
}
</script>

// 组件 B 
<script setup>
import mitt from './mitt'
import { onUnmounted } from 'vue'
const someMethed = () => { ... }
mitt.on('handleChange',someMethed)
onUnmounted(()=>{
    mitt.off('handleChange',someMethed)
})
</script>

Vue2.xコンポーネント通信は12種類あります

  1. 小道具

  2. $emit / v-on

  3. .sync

  4. v モデル

  5. 参照

  6. リスナー

  7. 提供する/注入する

  8. イベントバス

  9. Vuex

  10. $root

  11. スロット

親子コンポーネント通信は次のように使用できます。

  • 小道具

  • $emit / v-on

  • リスナー

  • 参照

  • .sync

  • v モデル

兄弟コンポーネント通信は次のように使用できます。

  • イベントバス

  • Vuex

  • $parent

クロスレベルのコンポーネント通信は以下を使用して実行できます。

  • 提供/注入

  • イベントバス

  • Vuex

  • リスナー

  • $root

readonly でラップされたデータは深いレベルで読み取り専用となり、shallowReadonly の最外層のみが読み取り専用になります。

toRaw と markRaw

toRaw は、リアクティブ関数または読み取り専用関数から変換された共通オブジェクトを応答プロキシに変換できます。共通オブジェクトのプロパティ値が変更されても、ビュー インターフェイスは更新されません。通常、不変のデータ ソースを含む大きなリストをレンダリングするために使用され、プロキシ変換をスキップするとパフォーマンスが向上します。

markRaw は、応答データに変換されないようにオブジェクトをマークし、オブジェクト自体を返すことしかできません。通常、サードパーティのクラス インスタンスや vue オブジェクトなど、応答として設定すべきではない特定の値に使用されます。

markRaw は、決して応答しないデータをマークします。後でリアクティブに変換されたとしても、応答しません。

toRef は、応答オブジェクトのプロパティの Ref 参照を作成します。更新するとき、参照オブジェクトは同期的に更新されます。toRef によって作成されたデータが変更されても、ビュー インターフェイスの更新はトリガーされないことに注意してください。 toRef はリファレンスであり、生データがリンクされています。

オブジェクトから属性を取得し、この属性を操作します

toRef を使用してリアクティブにします

let count =toRef(objA,'count')
          count.value++

著者に連絡するためのグループを追加 vx: xiaoda0423

ウェアハウスのアドレス: https://github.com/webVueBlog/WebGuideInterview

おすすめ

転載: blog.csdn.net/qq_36232611/article/details/130716887