kbone 高階 - 使用小程式內建元件(一)

千鋒Python唐小強發表於2020-08-13

1、用法

需要明確的是,如果沒有特殊需求的話,請儘量使用 html 標籤來編寫程式碼,使用內建元件時請按需使用。這是因為絕大部分內建元件外層都會被包裹一層自定義元件,如果自定義元件的例項數量達到一定量級的話,理論上是會對效能造成一定程度的影響,所以對於 view、text、image 等會被頻繁使用的內建元件,如果沒有特殊需求的話請直接使用 div、span、img 等 html 標籤替代。

部分內建元件可以直接使用 html 標籤替代,比如 input 元件可以使用 input 標籤替代。目前已支援的可替代元件列表:


<
input /> --> input 元件

< input type= "radio" /> --> radio 元件
< input type= "checkbox" /> --> checkbox 元件
< label> < label> --> label 元件
< textarea> </ textarea> --> textarea 元件
< img /> --> image 元件
< video> </ video> --> video 元件
< canvas> </ canvas> --> canvas 元件

還有一部分內建元件在 html 中沒有標籤可替代,那就需要使用 wx-component 標籤或者使用 wx- 字首,基本用法如下:


<!-- wx-component 標籤用法 -->

< wx-component behavior= "picker" mode= "region" @ change= "onChange">選擇城市 </ wx-component>
< wx-component behavior= "button" open-type= "share" @ click= "onClickShare">分享 </ wx-component>

<!-- wx- 字首用法 -->
< wx-picker mode= "region" @ change= "onChange">選擇城市 </ wx-picker>
< wx-button open-type= "share" @ click= "onClickShare">分享 </ wx-button>
kbone 高階 - 使用小程式內建元件(一)

如果使用 wx-component 標籤表示要渲染小程式內建元件,然後 behavior 欄位表示要渲染的元件名;其他元件屬性傳入和官方文件一致,事件則採用 vue 的繫結方式。

wx-component 或 wx- 字首已支援內建元件列表:

  • cover-image 元件
  • cover-view 元件
  • movable-area 元件
  • movable-view 元件
  • scroll-view 元件
  • swiper 元件
  • swiper-item 元件
  • view 元件
  • icon 元件
  • progress 元件
  • text 元件
  • button 元件
  • editor 元件
  • form 元件
  • picker 元件
  • picker-view 元件
  • picker-view-column 元件
  • slider 元件
  • switch 元件
  • navigator 元件
  • camera 元件
  • image 元件
  • live-player 元件
  • live-pusher 元件
  • map 元件
  • ad 元件
  • official-account 元件
  • open-data 元件
  • web-view 元件

內建元件的子元件會被包裹在一層自定義元件裡面,因此內建元件和子元件之間會隔著一層容器,該容器會追加 h5-virtual 到 class 上(除了 view、cover-view、text、scroll-view 和 picker-view 元件外,因為這些元件需要保留子元件的結構,所以沿用 0.x 版本的渲染方式)。

0.x 版本:在 0.x 版本中,絕大部分內建元件在渲染時會在外面多包裝一層自定義元件,可以近似認為內建元件和其父級節點中間會多一層 div 容器,所以會對部分樣式有影響。這個 div 容器會追加一個名為 h5-xxx 的 class,例如使用 video 元件,那麼會在這個 div 容器上追加一個名為 h5-video 的 class,以便對其做特殊處理。另外如果是用 wx-component 或是 wx- 字首渲染的內建元件,會在容器追加的 class 是 h5-wx-component,為了更方便進行識別,這種情況會再在容器額外追加 wx-xxx 的 class。

生成的結構大致如下:


<!-- 原始碼 -->

< div>
  < canvas>
    < div> </ div>
    < div> </ div>
  </ canvas>
  < wx-map>
    < div> </ div>
    < div> </ div>
  </ wx-map>
  < wx-scroll-view>
    < div> </ div>
    < div> </ div>
  </ wx-scroll-view>
</ div>

<!-- 1.x 版本生成的結構 -->
< view>
  < canvas class= "h5-canvas wx-canvas wx-comp-canvas">
    < element class= "h5-virtual">
      < cover-view> </ cover-view>
      < cover-view> </ cover-view>
    </ element>
  </ canvas>
  < map class= "h5-wx-component wx-map wx-comp-map">
    < element class= "h5-virtual">
      < cover-view> </ cover-view>
      < cover-view> </ cover-view>
    </ element>
  </ map>
  < element class= "h5-wx-component wx-scroll-view">
    < scroll-view class= "wx-comp-scroll-view">
      < view> </ view>
      < view> </ view>
    </ scroll-view>
  </ element>
</ view>

<!-- 0.x 版本本生成的結構 -->
< view>
  < element class= "h5-canvas">
    < canvas class= "wx-comp-canvas">
      < cover-view> </ cover-view>
      < cover-view> </ cover-view>
    </ canvas>
  </ element>
  < element class= "h5-wx-component wx-map">
    < map class= "wx-comp-map">
      < cover-view> </ cover-view>
      < cover-view> </ cover-view>
    </ map>
  </ element>
  < element class= "h5-wx-component wx-scroll-view">
    < scroll-view class= "wx-comp-scroll-view">
      < view> </ view>
      < view> </ view>
    </ scroll-view>
  </ element>
</ view>

PS:button 標籤不會被渲染成 button 內建元件,同理 form 標籤也不會被渲染成 form 內建元件,如若需要請按照上述原生元件使用說明使用。
PS:因為自定義元件的限制,movable-area/movable-view、swiper/swiper-item、picker-view/picker-view-column 這三組元件必須作為父子存在才能使用,比如 swiper 元件和 swiper-item 必須作為父子元件才能使用,如:


<
wx-swiper>

  < wx-swiper-item>A </ wx-swiper-item>
  < wx-swiper-item>B </ wx-swiper-item>
  < wx-swiper-item>C </ wx-swiper-item>
</ wx-swiper>

PS:預設 canvas 內建元件的 touch 事件為通用事件的 Touch 物件,而不是 CanvasTouch 物件,如果需要用到 CanvasTouch 物件的話可以改成監聽 canvastouchstart、canvastouchmove、canvastouchend 和 canvastouchcancel 事件。
PS:原生元件的表現在小程式中表現會和 web 端標籤有些不一樣,具體可參考原生元件說明文件。
PS:原生元件下的子節點,div、span 等標籤會被渲染成 cover-view,img 會被渲染成 cover-image,如若需要使用 button 內建元件請使用 wx-component 或 wx- 字首。
PS:如果將外掛配置 runtime.wxComponent 的值配置為 noprefix,則可以用不帶字首的方式使用內建元件。
PS:某些 Web 框架(如 react)會強行將節點屬性值轉成字串型別。對於普通型別陣列(如 wx-picker 元件的 value 屬性),字串化會變成,連線,kbone 會自動做解析,開發者無需處理;對於物件陣列(如 wx-picker 元件的 range 屬性),如遇到被自動轉成字串的情況,開發者需要將此物件陣列轉成 json 串傳入。

2、案例

在 kbone-advanced 目錄下建立 03-native-components 目錄。本案例在這個目錄下實現。

2.1 建立 package.json

cd 
03-
native-components

npm init -y

編輯 package.json:

{

  "scripts": {
    "mp": "cross-env NODE_ENV=production webpack --config build/webpack.mp.config.js --progress --hide-modules"
 },
  "dependencies": {
    "vue": "^2.5.11"
 },
  "browserslist": [
    "> 1%",
    "last 2 versions",
    "not ie <= 8"
 ],
  "devDependencies": {
    "babel-core": "^6.26.0",
    "babel-loader": "^7.1.2",
    "babel-preset-env": "^1.6.0",
    "babel-preset-stage-3": "^6.24.1",
    "cross-env": "^5.0.5",
    "css-loader": "^0.28.7",
    "extract-text-webpack-plugin": "^3.0.2",
    "file-loader": "^1.1.4",
    "html-webpack-plugin": "^4.0.0-beta.5",
    "mini-css-extract-plugin": "^0.5.0",
    "optimize-css-assets-webpack-plugin": "^5.0.1",
    "stylehacks": "^4.0.3",
    "vue-loader": "^15.7.0",
    "vue-template-compiler": "^2.6.10",
    "webpack": "^4.29.6",
    "webpack-cli": "^3.2.3",
    "mp-webpack-plugin": "latest"
 }
}

安裝依賴包:

npm install

2.2 配置 webpack

在 03-native-components 目錄下建立 build 資料夾,在資料夾下建立 webpack.mp.config.js 檔案,內容如下:


const 
path 
= 
require('path')

const webpack = require('webpack')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const { VueLoaderPlugin } = require('vue-loader')
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin')
const MpPlugin = require('mp-webpack-plugin') // 用於構建小程式程式碼的 webpack 外掛

const isOptimize = false // 是否壓縮業務程式碼,開發者工具可能無法完美支援業務程式碼使用到的 es 特性,建議自己做程式碼壓縮

module.exports = {
  mode: 'production' ,
  entry: {
    index: path.resolve(__dirname, '../src/index/main.mp.js' ),
  },
  output: {
    path: path.resolve(__dirname, '../dist/mp/common' ), // 放到小程式程式碼目錄中的 common 目錄下
    filename: '[name].js' , // 必需欄位,不能修改
    library: 'createApp' , // 必需欄位,不能修改
    libraryExport: 'default' , // 必需欄位,不能修改
    libraryTarget: 'window' , // 必需欄位,不能修改
  },
  target: 'web' , // 必需欄位,不能修改
  optimization: {
    runtimeChunk: false , // 必需欄位,不能修改
    splitChunks: { // 程式碼分隔配置,不建議修改
      chunks: 'all' ,
      minSize: 1000 ,
      maxSize: 0 ,
      minChunks: 1 ,
      maxAsyncRequests: 100 ,
      maxInitialRequests: 100 ,
      automaticNameDelimiter: '~' ,
      name: true ,
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10
        },
        default: {
          minChunks: 2 ,
          priority: -20 ,
          reuseExistingChunk: true
        }
      }
    },

    minimizer: isOptimize ? [
      // 壓縮CSS
      new OptimizeCSSAssetsPlugin({
        assetNameRegExp: /\.(css|wxss)$/g,
        cssProcessor: require('cssnano'),
        cssProcessorPluginOptions: {
          preset: ['default', {
            discardComments: {
              removeAll: true ,
            },
            minifySelectors: false , // 因為 wxss 編譯器不支援 .some>:first-child 這樣格式的程式碼,所以暫時禁掉這個
          }],
        },
        canPrint: false
      }),
      // 壓縮 js
      new TerserPlugin({
        test: /\.js(\?.*)?$/i,
        parallel: true ,
      })
    ] : [],
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader'
        ],
      },
      {
        test: /\.vue$/,
        loader: 'vue-loader' ,
      },
      {
        test: /\.js$/,
        use: [
          'babel-loader'
        ],
        exclude: /node_modules/
      },
      {
        test: /\.(png|jpg|gif|svg)$/,
        loader: 'file-loader' ,
        options: {
          name: '[name].[ext]?[hash]'
        }
      }
    ]
  },
  resolve: {
    extensions: ['*', '.js' , '.vue' , '.json' ]
  },
  plugins: [
    new webpack.DefinePlugin({
      'process.env.isMiniprogram': process.env.isMiniprogram, // 注入環境變數,用於業務程式碼判斷
    }),
    new MiniCssExtractPlugin({
      filename: '[name].wxss' ,
    }),
    new VueLoaderPlugin(),
    new MpPlugin(require('./miniprogram.config.js')),
  ],
}

在 03-native-components 資料夾下建立 miniprogram.config.js 檔案,內容如下:


module.exports 
= 
{    

    origin: ' ,    
    entry: '/' ,    
    router: {        
        index: [
            '/' ,
        ],
    },    
    redirect: {        
        notFound: 'index' ,        
        accessDenied: 'index' ,
    },
    generate: {
    appWxss: 'none' ,
    // 構建完成後是否自動安裝小程式依賴。'npm':使用 npm 自動安裝依賴
        autoBuildNpm: 'npm'
    },
    runtime: {
        // wxComponent: 'noprefix' ,
    wxComponent: 'default'
    },
    app: {
        navigationBarTitleText: 'miniprogram-project' ,
    },    
    projectConfig: {
        appid: '' ,
    projectname: 'native-components' ,
    },    
    packageConfig: {
        author: 'Felixlu' ,
    },
}

2.3 建立 index.html

在 03-native-components 目錄下建立 index.html 檔案,內容如下:


<!DOCTYPE 
html>

< html lang= "en">
  < head>
    < meta charset= "utf-8">
    < meta name= "viewport" content= "width=device-width, initial-scale=1, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no,minimal-ui, viewport-fit=cover" />
    < meta content= "yes" name= "apple-mobile-web-app-capable"/>
    < meta content= "black" name= "apple-mobile-web-app-status-bar-style"/>
    < meta name= "format-detection" content= "telephone=no, email=no" />
    < title>vue </ title>
    < style type= "text/css">
      #app {
        font-family: 'Avenir', Helvetica, Arial, sans-serif;
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
        text-align: center;
        color: #2c3e50;
        margin-top: 60px;
     }
    </ style>
  </ head>
  < body>
    < div id= "app"> </ div>
  </ body>
</ html>

2.4 建立小程式入口檔案

在 03-native-components/src/index 目錄下建立 main.mp.js 小程式入口檔案,內容如下:


import Vue 
from 
'vue'

import App from './App.vue'

export default function createApp( ) {
  const container = document.createElement( 'div')
 container.id = 'app'
  document.body.appendChild(container)

  return new Vue({
    el: '#app',
    render: h => h(App)
 })
}


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69923331/viewspace-2711464/,如需轉載,請註明出處,否則將追究法律責任。

相關文章