本節概述
經過前面小節的學習,我們已經搭建起了小程序的編譯構建環境,能夠將我們開發的小程序項目編譯成為對應的邏輯代碼文件 logic.js
,頁面渲染文件 view.js
,樣式文件 style.css
和配置文件 config.json
在編譯小程序的過程中,我們是將小程序的 WXML
文件中的組件節點編譯為了 ui-xxx
格式的自定義組件,并將一些屬性和事件處理進行了轉化,這節開始我們就來針對性的完善我們的組件庫.
搭建環境
這里我們的小程序頁面渲染使用vue2來進行,首先我們使用vite來搭建一個開發環境,使用 @vitejs/plugin-vue2
來編譯組件內容,并將輸出為 iife
格式的文件: iife
格式的文件會將邏輯放在一個自執行的函數中進行,能夠有效的避免全局的變量的沖突等問題;
為了加載方便,我們還可以把組件的樣式也一起注入的輸出的JS文件中,這樣就可以少加載一個文件了。
import { defineConfig } from 'vite';
import path from 'path';
import vue from '@vitejs/plugin-vue2';
import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js'export default defineConfig({plugins: [vue(),cssInjectedByJsPlugin(), // 將css樣式注入到JS中],build: {lib: {entry: path.resolve(__dirname, './src/index.ts'),formats: ['iife'],name: 'components',},outDir: path.resolve(__dirname, 'dist'),rollupOptions: {external: ['vue']},},resolve: {extensions: ['.js', '.ts'],alias: {'@': path.resolve(__dirname, './src')}},
});
統一處理組件事件綁定
在上一節我們的編譯過程中,我們是將小程序的事件綁定如 bindtap="tapHandler(1, $event, true)"
編譯為了下面格式:
attrs {bindtap: { methodName: "tapHandler", params: [1, "$event", true] }
}
那么最終就得有組件庫來對這種 bind
開頭的屬性進行特殊處理,轉化為vue的事件處理邏輯,并調用bridge進行事件傳遞;
當然在處理過程中,我們還需要對事件對象進行包裝,小程序的事件對象里面主要有兩塊內容:
- detail 節點的數據, 如input輸入框的value等
- currentTarget.dataset 當前節點上攜帶的
data-*
數據
這部分邏輯是在每個組件都需要用到的,這里我們將其封裝為一個vue mixin
// 用于將屬性名稱轉化為駝峰命名格式: 如 `test-handler` => testHandler
function toCamelCase(attr: string) {return attr.toLowerCase().replace(/-(.)/g, function (_, group) {return group.toUpperCase();});
}// 獲取節點屬性上的 `data-*` 數據
function makeAttrParams(attrs: Record<string, any>) {const result = {};for (const attr in attrs) {if (!/^data-/.test(attr)) {continue;}const theAfter = attr.replace(/^data-/, '');const transAttr = toCamelCase(theAfter);result[transAttr] = attrs[attr];}return result;
}export const miniAppMixin = {created() {for (let attr in (this as any).$attrs) {if (!/^bind/.test(attr)) {continue;}if (!(this as any).$attrs[attr]) {continue;}// 獲取事件名稱,如 "tap" const eventName = attr.replace(/^bind/, '');const { methodName, params } = (this as any).$attrs[attr];// 這個會由 ui 線程創建Vue渲染實例的時候進行注入,用戶獲取到對應 bridge id,用于進行事件傳遞const { id } = (this as any).$vnode.context._bridgeInfo;(this as any).$on(eventName, (sysParams) => {// 構造小程序事件參數對象const _event = {detail: {...sysParams,},currentTarget: {dataset: makeAttrParams((this as any).$attrs)}};// 組裝事件參數const paramsList = params.map(param => {if (param === '$event') {return _event;}return param;});if (!paramsList.length) {paramsList.push(_event);}window.JSBridge.onReceiveUIMessage({type: 'triggerEvent',body: {methodName,id,paramsList}});});}}
}
編寫組件
這里我們以 view
組件為例,其他組件類似我們就不全部實現,大家可前往文末點擊本節代碼前往查看
<template><div class="ui-view" @click="clicked"><slot></slot></div>
</template><script>
import { miniAppMixin } from '@/mixins';export default {name: 'ui-view',mixins: [miniAppMixin],methods: {clicked() {this.$emit('tap');}}
}
</script><style lang="less" scoped>
.ui-view {display: block;
}
</style>
編寫完組件后我們需要在入口文件進行統一注冊:
import View from './components/view/index.vue';const components = {'ui-view': View,
};
Object.keys(components).forEach(name => {// vue會通過全局進行引入window.Vue.component(name, components[name]);
});
小程序頁面交互API
在小程序中,除了頁面上的組件以外,還有一部分API也是能夠控制頁面內容顯示的,如 showToast
能夠控制頁面彈出一個提示框,這部分作用于頁面渲染的api我們也實現在組件庫中。
interface ToastInfo {dom: HTMLElement | null;timer: number | null;
}
const toastInfo: ToastInfo = {dom: null,timer: null,
}export function showToast(opts) {const title = opts.title;const duration = opts.duration || 1500;const icon = opts.icon || 'success';if (!title) return;// 移除舊的toastif (toastInfo.dom) {document.body.removeChild(toastInfo.dom);toastInfo.dom = null;clearTimeout(toastInfo.timer!);}// 創建toast組件的容器,并按照icon添加不同的狀態類名toastInfo.dom = document.createElement('div');toastInfo.dom.classList.add('ui-toast', `ui-toast--${icon}`);toastInfo.dom.innerHTML = `<p>${title}</p>`;document.body.appendChild(toastInfo.dom);document.body.appendChild(toastInfo.dom);toastInfo.timer = setTimeout(() => {document.body.removeChild(toastInfo.dom!);}, duration) as unknown as number;
}
將API掛載到全局對象上:
import { showToast } from './showToast';window.wxComponentsApi = {showToast,
}
這樣我們的組件包就開發好啦,文章中組件只以 view
組件為例進行了實現,其他組件可前往本節代碼倉庫。
本節代碼已上傳至 Github: mini-wx-app