欧美三级国产三级日韩三级_亚洲熟妇丰满大屁股熟妇_欧美亚洲成人一区二区三区_国产精品久久久久久模特

技術(shù)提煉|盤點那些Vue項目中的優(yōu)秀實踐-小程序篇 - 新聞資訊 - 云南小程序開發(fā)|云南軟件開發(fā)|云南網(wǎng)站建設(shè)-昆明葵宇信息科技有限公司

159-8711-8523

云南網(wǎng)建設(shè)/小程序開發(fā)/軟件開發(fā)

知識

不管是網(wǎng)站,軟件還是小程序,都要直接或間接能為您產(chǎn)生價值,我們在追求其視覺表現(xiàn)的同時,更側(cè)重于功能的便捷,營銷的便利,運營的高效,讓網(wǎng)站成為營銷工具,讓軟件能切實提升企業(yè)內(nèi)部管理水平和效率。優(yōu)秀的程序為后期升級提供便捷的支持!

您當(dāng)前位置>首頁 » 新聞資訊 » 小程序相關(guān) >

技術(shù)提煉|盤點那些Vue項目中的優(yōu)秀實踐-小程序篇

發(fā)表時間:2021-1-5

發(fā)布人:葵宇科技

瀏覽次數(shù):90

這一篇我們來聊聊小程序,這里的小程序開發(fā)使用的是uniapp。

cross-env切換環(huán)境

在pc端的vue項目中,我們通常會使用vue的環(huán)境變量來控制程序在不同環(huán)境的切換,例如:

目錄結(jié)構(gòu):

├── .env.development
├── .env.test
├── vue.config.js
復(fù)制代碼

vue.config.js:

module.exports = {
  devServer: {
    port: 8062,
    proxy: {
      '/api': {
        target: process.env.BASEURL,
        pathRewrite: {
          '^/api': '/api'
        }
      }
    }
  }
}
復(fù)制代碼

.env.development:

BASEURL=http://developapi.com/
復(fù)制代碼

.env.test:

BASEURL=http://testapi.com/
復(fù)制代碼

這樣我們只需要通過package.json里不同環(huán)境的script運行,就可以切換到想要的環(huán)境。在小程序里,我們通常都是在微信開發(fā)工具里編譯運行,這個時候我們就可以借助cross-envscript來進行環(huán)境的切換。

首先,我們需要先安裝cross-env

npm install --save-dev cross-env
復(fù)制代碼

然后我們改寫package.json的script,讓我們在運行script的時候額外用node執(zhí)行一個js文件

目錄結(jié)構(gòu):

├── script                        
│   └── build.js      # 用來修改baseurl的腳本文件
├── utils
│   ├── http.js       # 對axios的再次封裝,引入config中的url                      
│   └── config.js     # 指定不同環(huán)境的baseurl
├── package.json
├── manifest.json
復(fù)制代碼

package.json:

"scripts": {
  "dev": "cross-env NODE_ENV=development node ./script/build.js",
  "test": "cross-env NODE_ENV=test node ./script/build.js",
  "pro": "cross-env NODE_ENV=production node ./script/build.js"
}
復(fù)制代碼

build.js:

const fs = require('fs')
const path = require('path')
const manifest = require("../manifest.json")
const config = require("../utils/config.json")
switch (process.env.NODE_ENV) {
  case 'development':
    manifest["mp-weixin"].appid = 'somewxid1'
    config.DEV = 'https://devapi1.com/miniapp/'
    config.PRO = 'https://proapi1.com/miniapp/'
    break;
  case 'test':
    manifest["mp-weixin"].appid = 'somewxid2'
    config.DEV = 'https://devapi2.com/miniapp/'
    config.PRO = 'https://proapi2.com/miniapp/'
    break;
  case 'production':
    manifest["mp-weixin"].appid = 'somewxid3'
    config.DEV = 'https://devapi3.com/miniapp/'
    config.PRO = 'https://proapi3.com/miniapp/'
    break;
}

try {
  fs.writeFileSync(path.resolve(__dirname, '../manifest.json'), JSON.stringify(manifest, null, 4))
  fs.writeFileSync(path.resolve(__dirname, '../utils/config.json'), JSON.stringify(config, null, 4))
} catch (error) {
  console.error(error)
}

console.log('修改成功')
復(fù)制代碼

config.json:

{
  "DEV": "https://devapi1.com/miniapp/",
  "PRO": "https://proapi1.com/miniapp/"
}
復(fù)制代碼
移動端開發(fā)中的css

設(shè)計規(guī)范

在移動端開發(fā)中,我們通常會有一份設(shè)計規(guī)范,這份規(guī)范通常會包含一下內(nèi)容:

  • 項目中所使用的字體大小樣式及其對應(yīng)應(yīng)用場景
  • 項目中使用到的顏色及其對應(yīng)場景
  • 項目中一些通用組件的樣式

對于這個規(guī)范,我們的最佳實踐方式是用一個css預(yù)編譯器文件把這些樣式寫成常量,在頁面中直接取用,例如:

// mixins
.ellipsis(@line: 2) {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: @line;
  overflow: hidden;
}
// Color Palette
@primary: #E20000;
@black: #000;
@white: #fff;
@gray-1: #f7f8fa;
@gray-2: #f2f3f5;
@gray-3: #ebedf0;
@gray-4: #dcdee0;
@gray-5: #c8c9cc;
@gray-6: #969799;
@gray-7: #646566;
@gray-8: #323233;
@red: #ee0a24;
@blue: #1989fa;
@orange: #ff976a;
@orange-dark: #ed6a0c;
@orange-light: #fffbe8;
@green: #07c160;

// Gradient Colors
@gradient-red: linear-gradient(to right, #ff6034, #ee0a24);
@gradient-orange: linear-gradient(to right, #ffd01e, #ff8917);

// Component Colors
@text-color: @gray-8;
@active-color: @gray-2;
@active-opacity: .7;
@disabled-opacity: .5;
@background-color: @gray-1;
@background-color-light: #fafafa;

// Padding
@padding-base: 4px;
@padding-xs: @padding-base * 2;
@padding-sm: @padding-base * 3;
@padding-md: @padding-base * 4;
@padding-lg: @padding-base * 6;
@padding-xl: @padding-base * 8;

// Font
@font-size-xs: 10px;
@font-size-sm: 12px;
@font-size-md: 14px;
@font-size-lg: 16px;
@font-weight-bold: 500;
@price-integer-font-family: Avenir-Heavy, PingFang SC, Helvetica Neue, Arial, sans-serif;

// Animation
@animation-duration-base: .3s;
@animation-duration-fast: .2s;

// Border
@border-color: @gray-3;
@border-width-base: 1px;
@border-radius-sm: 2px;
@border-radius-md: 4px;
@border-radius-lg: 8px;
@border-radius-max: 999px;
復(fù)制代碼

相對布局

適配不同手機的css顯示,無疑是一項繞不開的課題,這里主要想說說如何用好相對布局。

px,em,rem

px:

  • 解釋:px像素(Pixel)。相對長度單位。像素px是相對于顯示器屏幕分辨率而言的。

em:

  • 解釋:em是相對長度單位。相對于當(dāng)前對象內(nèi)文本的字體尺寸。如當(dāng)前對行內(nèi)文本的字體尺寸未被人為設(shè)置,則相對于瀏覽器的默認(rèn)字體尺寸。
  • 缺點:em會繼承父級元素的字體大?。▍⒖嘉锸歉冈氐膄ont-size)所以會逐級嵌套計算。

rem:

  • 解釋:rem是css3中提出來基于em的優(yōu)化,rem依舊是相對長度單位,但它相對的是根元素。
  • 優(yōu)點:只修改根元素就成比例地調(diào)整所有字體大小,避免字體大小逐層復(fù)合的連鎖反應(yīng)。

合理的使用px,em,rem可以幫助我們更好的控制大小。在各大小程序里也有諸如rpx這樣小程序提供的相對長度單位可以使用。

百分比和flex

在實現(xiàn)相對布局上,早時候我們最常使用百分比結(jié)合行內(nèi)元素的方法,但這類方法的缺點也十分明顯,當(dāng)我們行內(nèi)的元素變多,我們需要手動重新計算百分比,動態(tài)的增減元素也需要重新計算,所以flex就變得更加受追捧。

有關(guān)flex的教程,推薦大家看一看阮大神的這篇博客。


分享生成海報

大多數(shù)小程序都有分享進行推廣的業(yè)務(wù)場景,雖然小程序自帶分享卡片的功能,但因為它的不夠直觀和相對死板,實際開發(fā)中我們更多會使用生成海報分享。對于一些電商小程序,生成的海報還會附帶一些額外的功能,例如用戶分銷上下級綁定,這里我們就來簡單介紹一個分享海報的實現(xiàn)。(因為這里我們只專注實現(xiàn)邏輯,所以css的部分就不做展示了)

目錄結(jié)構(gòu):

├── components                   # 全局公共組件    
│   ├── share-popup
│   │   └── share-popup.vue      # 選擇微信分享或海報分享上拉菜單
│   └── poster-item
│       └── poster-item.vue      # 生成海報組件
├── utils                 
│   └── message.js               # 指定分享參數(shù)
復(fù)制代碼

首先我們先來看看share-popup:

<template>
  <uni-popup ref="popup">
    <div class="icon-content">
      <div class="item">
        <img class="icon" src="http://www.wxapp-union.com/~@/static/poster.png" @click="clickPoster"/>
        <span>生成海報</span>
      </div>
      <div class="item" @click="close">
        <button
          class="share-btn"
          open-type="share"
          :data-title="shareInfo.name"
          :data-imgurl="shareInfo.image"
          :data-path="shareInfo.path">
          <img class="icon" src="http://www.wxapp-union.com/~@/static/wechat_icon.png" />
        </button>
        <span>分享到微信</span>
      </div>
    </div>
  </uni-popup>
</template>
<script>
export default {
  name: 'share-popup',
  props: {
    shareInfo: {
      type: Object,
      defalut () {
        return {
          title: '',
          path: '',
          image: ''
        }
      }
    }
  },
  data () {
    return {
    }
  },
  computed: {
		info () {
			return this.$store.state.user.userInfo
    }
  },
  methods: {
    clickPoster () {
      this.$emit('sharePoster')
      this.close()
    },
    open() {
      this.$refs.popup.open()
    },
    close() {
      this.$refs.popup.close()
    }
  }
}
</script>
復(fù)制代碼

分享到微信其實就是用了微信button的開放接口,這里的關(guān)鍵在于我們調(diào)用組件時傳入的參數(shù)。

接下來我們看下poster-item,在這個組件里,我們將會在海報中展示這些信息:

  1. 用戶昵稱
  2. 用戶頭像
  3. 設(shè)計好的海報背景圖
  4. 小程序分享二維碼
  5. 海報中需要展示的分享詳情(這里是商品價格、劃線價、名稱)
<template>
  <uni-popup ref="popup" :maskClick="false" :animation="false">
    <div class="flex column">
      <div class="btn-close-wrapper">
        <div class="btn-close" @click="close"></div>
      </div>
      <canvas class="canvas-code" canvas-id="canvas" style="width: 300px;height: 452px;">
      </canvas>
      <div class="btn-save" @click.stop="save">保存到相冊</div>
    </div>
  </uni-popup>
</template>
<script>
const promisify = (fn) => {
  return function(args = {}) {
    return new Promise((resolve, reject) => {
      fn.call(null, {
        ...args,
        success (data) {
          console.log('data', data)
          resolve(data)
        },
        fail (err) {
          console.log('err', err)
          reject(err)
        }
      })
    })
  }
}
const downloadFile = promisify(uni.downloadFile)
export default {
  name: 'poster-item',
  props: {
    item: {
      type: Object,
      defalut () {
        return {
          realPrice: '0.00',
          price: '0.00',
          name: '',
          image: ''
        }
      }
    },
    info: {
      page: '',
      scene: ''
    }
  },
  data () {
    return {
      imgSrc: '',
      ctx: {}
    }
  },
  computed: {
    username () {
      return this.$store.state.user.userInfo.nickname
    },
    avatar () {
      const avatar = this.$store.state.user.userInfo.avatar
      if (/^http/.test(avatar)) {
        return avatar
      }
      return false
    }
  },
  methods: {
    draw ({ realPrice, price, name, username, itemImage, avatar, qrcode }) {
      const ctx = this.ctx
      ctx.drawImage(itemImage,0, 0, 300, 300);
      ctx.setFillStyle('#F0F0F0')
      ctx.fillRect(0, 300, 300, 152)
      ctx.drawImage(require("@/static/bg.png"), 0, 322, 202, 131);
      
      ctx.font = 'bold 14px "HelveticaNeue-Bold,HelveticaNeue"'
      ctx.setFillStyle('#D0021B')
      ctx.fillText('¥', 24, 334)
      ctx.setFontSize(20)
      // realPrice
      ctx.fillText(realPrice, 38, 334)
      if (realPrice < price) {
        const w1 = ctx.measureText(realPrice).width
        ctx.font = 'normal 12px "HelveticaNeue"'
        ctx.setFillStyle('#4A4A4A')
        ctx.fillText(`¥${price}`, 40 + w1, 334)
        const w2 = ctx.measureText(`¥${price}`).width
        ctx.beginPath()
        ctx.moveTo(42 + w1, 330)
        ctx.lineTo(42 + w1 + w2, 330)
        ctx.stroke()
        ctx.closePath()
      }
      
      ctx.font = '12px PingFangSC-Regular,PingFang SC'
      ctx.setFillStyle('#4A4A4A')
      
      ctx.fillText(name.substring(0, 15), 24, 356, 152)
      ctx.fillText(name.substring(15, 30), 24, 372, 152)
      
      ctx.drawImage(qrcode, 205, 322, 67, 67);
      ctx.setFillStyle('#4A4A4A')
      ctx.fillText('長按識別', 216, 416, 67)
      
      ctx.save()
      
      const self = this
      ctx.draw(true, setTimeout((e) => {
        uni.canvasToTempFilePath({
          canvasId: 'canvas',
          success ({ tempFilePath }) {
            self.imgSrc = http://www.wxapp-union.com/tempFilePath
          }
        }, self)
      }, 100))
    },
    open () {
      const { realPrice, price, name, image } = this.item
      this.$refs.popup.open()
      uni.showLoading()
      Promise.all([
        downloadFile({ url: image }), // 商品圖
        downloadFile({ url: this.avatar || image }), // 用戶頭像
        // 從后臺獲取二維碼
        downloadFile({ url: `someapi/getWxacode?page=${this.info.page}&scene=${encodeURIComponent(this.info.scene)}`, header: {'Authorization': `Bearer ${uni.getStorageSync('token')}` } })
      ]).then(([
        { tempFilePath: itemImage },
        { tempFilePath: avatar },
        { tempFilePath: qrcode }
      ]) => {
        uni.hideLoading()
        this.draw({ realPrice, price, name, username: this.username, itemImage, avatar, qrcode })
      })
    },
    close () {
      this.$refs.popup.close()
      this.ctx.clearRect(0, 0, 300, 452)
    },
    save () {
      const self = this
      uni.showLoading()
      uni.saveImageToPhotosAlbum({
        filePath: this.imgSrc,
        success () {
          uni.showToast({ title: '保存成功' })
          self.close()
        },
        complete (res) {
          uni.hideLoading()
          console.log('complete', res)
        }
      })
    }
  },
  mounted () {
    this.ctx = uni.createCanvasContext('canvas', this)
  }
}
</script>
復(fù)制代碼

這里我們主要是利用了canvas進行海報的繪制。二維碼實現(xiàn)用戶分銷綁定的原理是二維碼包含的跳轉(zhuǎn)鏈接里有參數(shù),在訪問這些頁面的時候,我們可以提前獲取這些參數(shù),完成綁定邏輯。

在完成了這兩個組件后,我們會在頁面中這樣使用:

<template>
  <div>
    <!-- some other content -->
    <button @click="showShare">點擊分享</button>
    <share-popup ref="share" @sharePoster="showPoster">
    </share-popup>
    <poster-item
      ref="poster"
      :item="{
        realPrice: product.realPrice,
        price: product.price,
        name: product.title,
        image: product.icons[0]
      }"
      :info="posterInfo">
    </poster-item>
  </div>
</template>
<script>
export default {
  data () {
    return {
      product: {}
    }
  },
  computed: {
    query () {
      return {
        id: this.productId,
        memberId: this.$store.state.user.userInfo.id
      }
    },
    posterInfo () {
      return this.$message.makePoster(this.query)
    }
  },
  async onLoad(query){
    // query中包含我們二維碼里的參數(shù)
    // 可以利用query里的值完成綁定
  },
  onShareAppMessage() {
    return {
      title: this.product.title,
      imageUrl: this.product.icons[0],
      path: this.$message.makeShare(this.query)
	  }
  },
  methods: {
    showPoster () {
      this.$refs.poster.open()
    },
    showShare () {
      this.$refs.share.open()
    }
  }
}
</script>
復(fù)制代碼

在頁面使用的時候,我們其實做了兩件事:

  • 頁面加載的時候獲取二維碼里攜帶的信息
  • 將需要的信息傳遞給poster-item組件

這里對信息的處理,我們用了$message方法做了個過濾,下面看看這個方法:

const _dealPath = (path) => {
  if (path) {
    return path
  }
  const pages = getCurrentPages()
  console.log('_dealPath', pages[pages.length - 1].route)
  return pages[pages.length - 1].route
}
const message = {
  array: [],
  register ({ page, keys }) {
    this.array.push({ page, keys})
  },
  makeShare (params, path) {
    path = _dealPath(path)
    const index = this.array.findIndex(item => item.page === path)
    if (index > -1) {
      const { page, keys } = this.array[index]
      const query = keys.map(_key => {
        return `${_key}=${params[_key]}`
      }).join('&')
      console.log(`makeShare page: ${page}, query: ${query}`)
      return `/${page}?${query}`
    }
    return ''
  },
  makePoster (params, path) {
    path = _dealPath(path)
    const index = this.array.findIndex(item => item.page === path)
    if (index > -1) {
      const { page, keys } = this.array[index]
      const scene = keys.map(_key => {
        return params[_key]
      }).join('&')
      console.log(`makePoster page: ${page}, scene: ${scene}`)
      return { page, scene }
    }
    return null
  },
  resolveQuery (query) {
    const path = _dealPath()
    const index = this.array.findIndex(item => item.page === path)
    if (index > -1) {
      const { keys } = this.array[index]
      // 如果是海報
      if (query.scene) {
        const values = decodeURIComponent(query.scene).split('&')
        let res = {}
        keys.forEach((key, index) => {
          res[key] = values[index]
        })
        return res
      }
      // 如果是分享
      if (Object.keys(query).length === keys.length) {
        return query
      }
      return false
    }
    return false
  }
}
message.register({ page: 'pages/Search/detail', keys: ['id', 'memberId', 'timestamp'] })
message.register({ page: 'pages/Home/index', keys: ['memberId'] })
export default message
復(fù)制代碼

這個方法的目的是指定不同的跳轉(zhuǎn)頁面生成二維碼需要的參數(shù),并進行拼接。使用這個方法的好處在于,以后我們可能會有很多頁面需要有生成分享海報的功能,僅僅是每個頁面上調(diào)用一個拼接參數(shù)的函數(shù),會導(dǎo)致我們遺漏或多傳遞了參數(shù),用這個函數(shù)進行過濾可以提前檢測我們傳遞的參數(shù)是否正確。

uniapp分包

由于小程序有體積和資源加載限制,所以小程序平臺提供了分包方式,優(yōu)化小程序的下載和啟動速度。

所謂的主包,即放置默認(rèn)啟動頁面/TabBar 頁面,以及一些所有分包都需用到公共資源/JS 腳本;而分包則是根據(jù)pages.json的配置進行劃分。

在小程序啟動時,默認(rèn)會下載主包并啟動主包內(nèi)頁面,當(dāng)用戶進入分包內(nèi)某個頁面時,會把對應(yīng)分包自動下載下來,下載完成后再進行展示。此時終端界面會有等待提示。

注意點:

  • subPackages 里的pages的路徑是 root 下的相對路徑,不是全路徑。
  • 微信小程序每個分包的大小是2M,總體積一共不能超過16M。
  • 百度小程序每個分包的大小是2M,總體積一共不能超過8M。
  • 支付寶小程序每個分包的大小是2M,總體積一共不能超過4M。
  • QQ小程序每個分包的大小是2M,總體積一共不能超過24M。
  • 分包下支持獨立的 static 目錄,用來對靜態(tài)資源進行分包。
  • 分包是按照分包的順序進行打包的,所有的subpackages配置以外的文件路徑,全部都被打包在主包(App)內(nèi)。
  • subpackages無法嵌入另一個subpackages。
  • tabBar頁面必須在App主包內(nèi)。

支持分包的目錄結(jié)構(gòu):

┌─pages               
│  ├─index
│  │  └─index.vue    
│  └─login
│     └─login.vue    
├─pagesA   
│  ├─static
│  └─list
│     └─list.vue 
├─pagesB    
│  ├─static
│  └─detail
│     └─detail.vue  
├─static             
├─main.js       
├─App.vue          
├─manifest.json  
└─pages.json 
復(fù)制代碼

pages.json:

{
  "pages": [{
    "path": "pages/index/index",
    "style": { ...}
  }, {
    "path": "pages/login/login",
    "style": { ...}
  }],
  "subPackages": [{
    "root": "pagesA",
    "pages": [{
      "path": "list/list",
      "style": { ...}
    }]
  }, {
    "root": "pagesB",
    "pages": [{
      "path": "detail/detail",
      "style": { ...}
    }]
  }],
  // 預(yù)加載
  "preloadRule": {
    "pagesA/list/list": {
      "network": "all",
      "packages": ["__APP__"]
    },
    "pagesB/detail/detail": {
      "network": "all",
      "packages": ["pagesA"]
    }
  }
}

相關(guān)案例查看更多