本配信活動:ファンの同級生無料でお送りします。
締切:2023年5月18日19時
下部のコメント欄に参加してトーク:読書の成果をコメントで共有してください。
フロントエンドエンジニアリング: Vue.js 3.0に基づいた設計と実践
ページビュー(ページビュー、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
自動化の形で、これらの埋もれたポイントを追加するにはどうすればよいでしょうか?
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),
},
};
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: true
JavaScript 構文が 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 を含むリアクティブ オブジェクトを作成しましたstate
。state.count
次に、 と をそれぞれ使用してカウンタの値にアクセスしますtoRaw(state).count
。カウンタをインクリメントすると、state.count
の値は 1 に更新されますが、toRaw(state).count
反応性システムによって管理されないため、初期値 0 のままになります。
このコードでは、関数params
を使用して変数をコピーしtoRaw
、Object.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'
更新するためにイベントがトリガーされるため、コンポーネントのテンプレート内の計算されたプロパティにバインドされた要素は自動的に更新に応答します。modelValue
pager
という名前の定数を定義しますemit
。
defineEmits()
関数は、コンポーネントが発行できるイベントをタイプセーフに定義するために使用されます。この例では、と のdefineEmits()
2 つのイベントが定義されており、それぞれコンポーネント トリガーイベントとプロパティ更新イベントを表します。'change'
'update:modelValue'
change
modelValue
defineEmits()
この関数は、定義されたすべてのイベントとそのパラメーター タイプを指定する汎用パラメーターを受け入れます。この例では、ジェネリック パラメーターは、change
イベントにはパラメーターがなく、update:modelValue
イベントにはvalue
type のパラメーターがあることを指定しています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 つのプロパティが含まれます。
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
defineProps
defineEmits
完全な型推論があり、 直接 利用できます<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
を使用する必要がある場合は、と を置き換える必要がありますslots
attrs
useSlots
useAttrs
インポートする必要があります: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.$router
this.$route
推奨事項: andの代わりにuseRoute
関数と関数を使用してください。useRouter
this.$route
this.$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
保存します。代码标记:实际渲染的语言文案
フロントエンド開発では、補助プラグイン [@mango-scripts/i18n-scripts] を使用して、言語パックをワンクリックでダウンロードし、ビジネス システムにインポートします本地代码
。
本地代码
言語パックをワンクリックでダウンロードしてビジネスシステムにインポートするための補助プラグイン [@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種類あります
小道具
$emit / v-on
.sync
v モデル
参照
親
リスナー
提供する/注入する
イベントバス
Vuex
$root
スロット
親子コンポーネント通信は次のように使用できます。
小道具
$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