亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

用 vue2 和 webpack 快速建構(gòu) NW.js 項(xiàng)目(2)

wmui / 3569人閱讀

摘要:再用使用進(jìn)行圖標(biāo)替換,建議尺寸是。同時(shí)為其添加管理員權(quán)限。用把之前臨時(shí)放在中的包拷貝到目錄,再根據(jù)文件寫(xiě)更新信息到中。這兒應(yīng)該可以優(yōu)化,下載到用戶數(shù)據(jù)目錄,或者其他臨時(shí)目錄。

打包NW.js應(yīng)用和制作windows安裝文件

更新:
此文章部分技術(shù)點(diǎn)已落后,可以查看 最新文章

這可能是中文史上最詳細(xì)的 NW.js 打包教程

本文適應(yīng)有一定 js 基礎(chǔ),第一次玩 windows 下 setup 打包的同學(xué),默認(rèn)的環(huán)境 windows。然后,文章太過(guò)于詳實(shí),看完會(huì)耗費(fèi)大量時(shí)間,暫時(shí)不想實(shí)操的,我會(huì)直接提供一個(gè) vue-nw-seed 種子項(xiàng)目,包含了當(dāng)前文章的一些優(yōu)化點(diǎn)。

本文涉及到的點(diǎn):

Node.js 打包 zip 、文件處理、crypto 提取 MD5 、iconv 處理字符串等

Resource Hacker 配置應(yīng)用的權(quán)限、圖標(biāo)、版權(quán)等

InnoSetup 制作安裝包、iss 文件配置

NW.js 應(yīng)用的更新(增量、全量更新)

...

未涉及到的點(diǎn):

代碼加密,本著前端的心態(tài)做的桌面端應(yīng)用,代碼 Uglify 后就已經(jīng)不可看了。如果有機(jī)密代碼或者加密算法等需要另外考慮,不在本文的討論范圍,提供一個(gè)官方文檔 Protect JavaScript Source Code

一、折騰能力強(qiáng),直接上文檔

How-to-package-and-distribute-your-apps

setup-on-windows

這部分沒(méi)啥好說(shuō)的,都很簡(jiǎn)單。

對(duì)新手友好。。。還有個(gè) NW.js 的打包在 gayhub 上還專門(mén)有個(gè) npm 包 nw-builder ,這個(gè)用起來(lái)就更簡(jiǎn)單了,我連示例都不想寫(xiě)的那種簡(jiǎn)單。然后這兒需要下載 NW.js 的 SDK 或者 NORMAL 的包,方法同我上一篇文章 用 vue2 和 webpack 快速建構(gòu) NW.js 項(xiàng)目 中 網(wǎng)絡(luò)不太好 部分

二、自助打包

NW.js 被打包出來(lái)后是一個(gè)文件夾,里面有整個(gè) runtime 和一個(gè) exe 文件,這時(shí)候整個(gè)打包就成功了,差不多有 100MB 左右。
但是,我們的應(yīng)用不再是給內(nèi)部使用,給用戶下載總不能直接給用戶拷貝一個(gè)文件夾或者下載 zip 壓縮包,那樣忒不靠譜的樣子,還以為是啥病毒呢。

我們能不能就像吃自助餐那樣,想吃啥就拿啥,想打包成啥樣就弄成啥樣。

實(shí)現(xiàn)思路
自己搞一個(gè) runtime,然后用 Node.js 對(duì)打包好的代碼進(jìn)行 zip 壓縮為 package.nw,然后放到 runtime 中,再用官方推薦的 InnoSetup 來(lái)打包成一個(gè) setup.exe。

1. XP 兼容性問(wèn)題

使用 NW.js 的主要優(yōu)勢(shì)是兼容 XP,教育行業(yè)這個(gè)真的很重要呀。。。
NW.js 不是全版本都支持 XP,由于 Chromium50 開(kāi)始就不支持XP了,所以如果你的客戶端要支持 XP,目前最佳的版本選擇是 0.14.7 。參見(jiàn) NW.js 的博客 NW.js v0.14.7 (LTS) Released

2. 制作一個(gè)自己的 runtime

從官網(wǎng) http://dl.nwjs.io/v0.14.7/ 下載一個(gè) normal 的包,然后在此基礎(chǔ)上進(jìn)行 DIY。

大概目錄就是這樣子

然后就開(kāi)始優(yōu)化和自定義工作:

1) 先整理下 locales 下的語(yǔ)言包,減少部分冗余。

2) 替換下 ffmpeg.dll 解決部分格式 video 的播放問(wèn)題等,下載的時(shí)候注意下版本,和 NW.js 相對(duì)應(yīng)就好。

3) 將 nw.exe 改名字為我們的應(yīng)用的名字,比如myProgramApp.exe,更正規(guī)一點(diǎn)。然后用 Resource Hacker 修改下版本和版權(quán)公司等相關(guān)信息。

4) 再用使用 Resource Hacker 進(jìn)行圖標(biāo)替換,建議尺寸是256。

5) 同時(shí)為其添加管理員權(quán)限。因?yàn)槲覀円鲈隽扛?,需要?Node.js 寫(xiě)文件到應(yīng)用所在目錄,當(dāng)安裝目錄是 C:Program Files 的時(shí)候,普通權(quán)限用戶沒(méi)有寫(xiě)權(quán)限。
具體操作還是用 Resource Hacker 打開(kāi)myProgramApp.exe,找到 Manifest

修改為

弄完了大概是這個(gè)樣子

3. 用 Node.js 打包 package.nw

需要一個(gè) zip 處理的依賴 archiver,第一次用這個(gè)依賴,建議直接去看他們的英文文檔,謹(jǐn)慎使用 bulk 這個(gè)方法,在 0.21.0 的時(shí)候就被廢棄了。
打包 zip 的方法大概就長(zhǎng)這樣:

const fs = require("fs")
const archive = require("archive")

function buildZipFile({ outZipPath, files, mainPackage } = {}) {
  let filesArr = Array.isArray(files) ? files : [files]

  // 創(chuàng)建一個(gè)可寫(xiě)流的 zip 文件
  var output = fs.createWriteStream(outZipPath)
  var archive = archiver("zip", { store: true })

  archive.on("error", console.error)

  // 打包 dist 目錄為 zip 壓縮包格式的 nw 文件
  archive.pipe(output)

  if (filesArr.length > 0) {
    filesArr.forEach(p => {
      if (!p) return

      // 剔除 package.json
      let hasPackJson = path.resolve(p, "package.json")
      if (fs.existsSync(hasPackJson)) fs.unlinkSync(hasPackJson)

      // 壓縮目錄
      archive.directory(p, "")
    })

    // 添加 package.json
    archive.file(mainPackage, { name: "package.json" })
  }

  archive.finalize()
}
4. InnoSetup 打包安裝包

Node.js 的豐富的生態(tài)已經(jīng)有人提供了一個(gè) node-innosetup-compiler 了,所以這個(gè)也很方便。不過(guò)對(duì)于我這種第一次玩這個(gè)的玩家還是有點(diǎn)懵逼,特別是那個(gè) iss 文件的編寫(xiě)。。。

鑒于本文不想寫(xiě)成 InnoSetup 的使用教程,所以只講講普通使用,如果你需要更復(fù)雜的功能,給你個(gè)文檔 Inno Setup Help

我提供一個(gè)我用的 setup.iss 文件,其中用下劃線開(kāi)頭(如: _appName )這種將會(huì)被 js 正則匹配掉

; Script generated by the Inno Setup Script Wizard.
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
; 該執(zhí)行目錄為 setup.iss 所在的目錄,請(qǐng)注意拼接相對(duì)目錄

#define MyAppName "_appName"
#define MyAppNameZh "_appZhName"
#define MyAppVersion "_appVersion"
#define MyAppPublisher "_appPublisher"
#define MyAppURL "_appURL"
#define MyAppExeName "_appName.exe"
#define OutputPath "_appOutputPath"
#define SourceMain "_appRuntimePath\_appName.exe"
#define SourceFolder "_appRuntimePath*"
#define LicenseFilePath "_appResourcesPathlicense.txt"
#define SetupIconFilePath "_appResourcesPath\_appName.ico"
#define MyAppId "_appId"

[Setup]
; NOTE: The value of AppId uniquely identifies this application.
; Do not use the same AppId value in installers for other applications.
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
AppId={#MyAppId}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
AppVerName={#MyAppName}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
DefaultDirName={pf}{#MyAppName}
LicenseFile={#LicenseFilePath}
OutputDir={#OutputPath}
OutputBaseFilename={#MyAppName}-v{#MyAppVersion}-setup
SetupIconFile={#SetupIconFilePath}
Compression=lzma
SolidCompression=yes
PrivilegesRequired=admin
Uninstallable=yes
UninstallDisplayName={#MyAppNameZh}
DefaultGroupName={#MyAppNameZh}

[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: checkedonce

[Files]
Source: {#SourceMain}; DestDir: "{app}"; Flags: ignoreversion
Source: {#SourceFolder}; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs

[Icons]
Name: "{commondesktop}{#MyAppNameZh}"; Filename: "{app}{#MyAppExeName}"; Tasks: desktopicon
Name: "{group}{#MyAppNameZh}"; Filename: "{app}{#MyAppExeName}"
Name: "{group}卸載{#MyAppNameZh}"; Filename: "{uninstallexe}"

[Languages]
Name: "chinese"; MessagesFile: "innosetupLanguagesChineseSimp.isl"

[Run]
Filename: "{app}{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, "&", "&&")}}"; Flags: nowait postinstall skipifsilent

創(chuàng)建一個(gè) resources 文件夾,里面放上 icon 和 license,就像這樣

再然后此 iss 配合 makeExeSetup 使用,格外酸爽,請(qǐng)忽略那一串 replace,233333333

// 新依賴,用于處理 utf 和 ansi 的字符串
const iconv = require("iconv-lite")

function makeExeSetup(opt) {
  const { issPath, outputPath, mainPackage, runtimePath, resourcesPath, appPublisher, appURL, appId } = opt
  const { name, appName, version } = require(mainPackage)
  const tmpIssPath = path.resolve(path.parse(issPath).dir, "_tmp.iss")
  const innosetupCompiler = require("innosetup-compiler")

  // rewrite name, version to iss
  fs.readFile(issPath, null, (err, text) => {
    if (err) throw err

    let str = iconv.decode(text, "gbk")
      .replace(/_appName/g, name)
      .replace(/_appZhName/g, appName)
      .replace(/_appVersion/g, version)
      .replace(/_appOutputPath/g, outputPath)
      .replace(/_appRuntimePath/g, runtimePath)
      .replace(/_appResourcesPath/g, resourcesPath)
      .replace(/_appPublisher/g, appPublisher)
      .replace(/_appURL/g, appURL)
      .replace(/_appId/g, appId)


    fs.writeFile(tmpIssPath, iconv.encode(str, "gbk"), null, err => {
      if (err) throw err

      // inno setup start
      innosetupCompiler(tmpIssPath, { gui: false, verbose: true }, function(err) {
        fs.unlinkSync(tmpIssPath)
        if (err) throw err
      })
    })
  })
}

這個(gè)時(shí)候就能制作出一個(gè)安裝包了,就像這樣

然后是安裝的流程

安裝完成的目錄

三、炫酷的安裝界面

雖然 InnoSetup 簡(jiǎn)單好使,但是制作出來(lái)的安裝包的安裝界面默認(rèn)是 windows2000 的界面,那個(gè)丑那個(gè)老舊喲。。。

如果你的應(yīng)用只要能用就行了,那這一步已經(jīng)完全夠了。
但技術(shù)人怎么能不折騰,下面,我們來(lái)搞炫酷的安裝包的制作方法。

先擺一個(gè)被我模仿的例子 INNOSETUP 仿有道云安裝包界面,同時(shí)還有個(gè)參考資料:互聯(lián)網(wǎng)軟件的安裝包界面設(shè)計(jì)-Inno setup 真心吐個(gè)槽,這方面的資料真少。。。

我其實(shí)都按照已有的素材包寫(xiě)好了一個(gè)了,但我們的 ui 還沒(méi)設(shè)計(jì)出更漂亮的安裝界面出來(lái),所以,我就暫時(shí)不放相關(guān)資源和效果了。

四、應(yīng)用的更新

這一塊,應(yīng)該是最輕松的,蛤。

我們的更新策略分為兩種,一種是只更新我們的業(yè)務(wù)代碼,每次只需要下載1MB多的業(yè)務(wù)代碼就搞定,走增量更新渠道;另一種是更新了我們的 runtime ,或者其他啥玩意的重要更新,需要全量更新,走全量更新的渠道。

實(shí)現(xiàn)思路
在打包的時(shí)候把版本和更新信息寫(xiě)入到 update.json 中,在每次客戶端打開(kāi)的時(shí)候都去請(qǐng)求這個(gè) json ,檢查 json 中版本和客戶端版本是否匹配,不匹配則根據(jù) json 中的約定規(guī)則進(jìn)行增量更新或全量更新。

1、準(zhǔn)備好更新文件

一個(gè)開(kāi)發(fā)原則是能懶就懶,能用工具做的就一定要用工具做。蛤蛤,在這個(gè)原則的堅(jiān)持下,我們來(lái)繼續(xù)優(yōu)化上文提到的打包建構(gòu)。

用 Node.js 把之前臨時(shí)放在 runtime 中的 package.nw (zip) 包拷貝到 output 目錄,再根據(jù) changelog.txt 文件寫(xiě)更新信息到 update.json 中。

準(zhǔn)備一個(gè) changelog.txt 文件在 config 配置目錄下,大概就長(zhǎng)這樣子,每次更新以--- 進(jìn)行分割,第一行是版本,后面是更新信息:

0.1.0
- 程序員 peter 開(kāi)始開(kāi)發(fā)了!
- 順便,請(qǐng)老板給 peter 漲工資。
---
1.0.0
- 客戶端正式版成功發(fā)布啦!
- 同時(shí),peter 因?yàn)橐鬂q工資已被打殘住院中,所以暫時(shí)不會(huì)有其他更新。
---

有同學(xué)問(wèn)我,為啥要這么設(shè)計(jì)個(gè) log.txt 出來(lái),不直接用 json 等其他形式進(jìn)行描述?
因?yàn)檫@個(gè)文件在未來(lái)可能要被打包到應(yīng)用中,連同 license 文件進(jìn)行打包;還有就是分離這部分描述,更易擴(kuò)展。

然后寫(xiě)一讀取這個(gè) log 的方法

function getLatestLogBycheckVersion({ changelogPath, mainPackage }) {
  // get package.json by package
  const packageJson = require(mainPackage)

  // check version
  // 大于等于3是因?yàn)楹戏ǖ陌姹拘畔⒆钌?"---" 有3個(gè)長(zhǎng)度
  const changeLogArr = fs.readFileSync(changelogPath, "utf-8").split("---").filter(v => v.trim().length >= 3)
  const latestInfo = changeLogArr.pop().split("
").map(v => v.trim()).filter(v => v.length)
  const version = latestInfo[0]

  if (packageJson.version !== version) {
    // 更新 package.json 的版本
    packageJson.version = version
    fs.writeFileSync(mainPackage, JSON.stringify(packageJson, null, "  "), "utf-8")
  }
  return latestInfo
}

// 這就是全局的 options
opt.latestLog = getLatestLogBycheckVersion(opt)

// 更新約定,用來(lái)判斷當(dāng)前版本是否需要增量更新
opt.noIncremental = process.argv.indexOf("--noIncremental") >= 0

增量更新的約定
通過(guò) process.argv 來(lái)檢測(cè)當(dāng)前是否需要增量更新,并寫(xiě)入到 options 中,這一點(diǎn)看起來(lái)有點(diǎn)稍微繁瑣,如果有其他更好的點(diǎn)子,歡迎踴躍來(lái)提 issue 或者直接私信我,謝謝!

接下來(lái)繼續(xù)處理打包完成的系列流程,需求是要移動(dòng) nw 到 output 目錄,還要寫(xiě)一個(gè) update.json

const crypto = require("crypto")

function finishedPackage(opt) {
  const { mainPackage, outputPath, latestLog, outZipPath, updateServerPath, noIncremental } = opt
  const { name, appName, version } = require(mainPackage)

  let versionCode = parseInt(version.replace(/./g, ""))
  let updateDesc = latestLog.slice(1).join("#%#")

  let outNWName = `${name}-v${version}.nw`
  let outNWPath = path.resolve(outputPath, outNWName)
  let updateJsonPath = path.resolve(outputPath, "update.json")

  // write update.json
  let updateJson = {
    appName,
    version,
    versionCode,
    requiredVersion: version,
    requiredVersionCode: versionCode,
    updateDesc,
    filePath: updateServerPath + outNWName,
    incremental: !noIncremental
  }

  // fileSize and MD5
  getMd5ByFile(outZipPath, (err, hexStr) => {
    if (err) throw err
    updateJson.MD5 = hexStr
    updateJson.fileSize = fs.statSync(outZipPath).size
    fs.writeFileSync(updateJsonPath, JSON.stringify(updateJson, null, "  "), "utf-8")

    copyFile(outZipPath, outNWPath)
    fs.unlink(outZipPath, err => err && console.error(err))
  })
}

function getMd5ByFile(filePath, callback) {
  let rs = fs.createReadStream(filePath)
  let hash = crypto.createHash("md5")
  rs.on("error", err => {
    if (typeof callback === "function") callback(err)
  })
  rs.on("data", hash.update.bind(hash))
  rs.on("end", () => {
    if (typeof callback === "function") callback(null, hash.digest("hex"))
  })
}

function copyFile(src, dst) {
  fs.createReadStream(src).pipe(fs.createWriteStream(dst))
}

整個(gè)打包完了差不多就這樣子了

那個(gè) update.json 里面的實(shí)際內(nèi)容就是這些

{
  "appName": "doudou",
  "version": "1.0.1-beta19",
  "versionCode": 101,
  "requiredVersion": "1.0.1-beta19",
  "requiredVersionCode": 101,
  "updateDesc": "- 程序員 peter 無(wú)話可說(shuō)",
  "filePath": "http://upgrade.iclassedu.com/doudou/upgrade/teacher/doudou-v1.0.1-beta19.nw",
  "incremental": true,
  "MD5": "9be46fc8fb04d38449eeb4358c3b5a31",
  "fileSize": 5469
}
2、獲取 update.json 并檢查更新

上代碼,代碼切換到 src 目錄中,在我們的應(yīng)用代碼中寫(xiě)上 utils/update.js 的相關(guān)方法。具體的幾個(gè)小方法,看注釋吧。

import { updateApi } from "config/app"
import { App } from "nw.gui"

const options = { method: "GET", mode: "cors", credentials: "include" }
let tmpUpdateJson = null

// 請(qǐng)求 update.json,返回的是 promise 類型的 json
export function getUpdateJson(noCache) {
  if (!noCache && tmpUpdateJson) return new Promise((resolve, reject) => resolve(tmpUpdateJson))
  return window.fetch(updateApi + "?" + (new Date().getTime()), options)
    .then(resp => resp.json())
    .then(json => {
      tmpUpdateJson = json
      return tmpUpdateJson
    })
}

// 檢查版本,如果有更新則跳轉(zhuǎn)到更新頁(yè)面
export function checkUpdate() {
  getUpdateJson().then(json => {
    if (json.version === App.manifest.version) return
    setTimeout(() => { window.location.hash = "/update" }, 500)
  })
}

然后在 main.js 中進(jìn)行更新檢查

// 優(yōu)先更新
import { checkUpdate } from "@/utils/update"
if (process.env.NODE_ENV !== "development") checkUpdate()
3、更新

在上面的基礎(chǔ)上做增量更新,基本思路就是用 Node.js 去下載 nw 包到應(yīng)用所在的目錄,并直接替換掉原有的 package.nw ,再重啟一下自己就搞定了;全量更新的話,就直接打開(kāi)應(yīng)用的下載頁(yè)面,讓用戶自行下載覆蓋安裝就搞定了。

// 下載 nw 包
export function updatePackage() {
  return new Promise((resolve, reject) => {
    getUpdateJson().then(json => {
      // 全量更新
      if (!json.incremental) {
        Shell.openExternal(getSetupApi)
        return reject({ message: "請(qǐng)下載最新版本,再覆蓋安裝" })
      }

      // 增量更新
      let packageZip = fs.createWriteStream(tmpNWPath)
      http
        .get(json.filePath, res => {
          if (res.statusCode < 200 || res.statusCode >= 300) return reject({ message: "下載出錯(cuò),請(qǐng)稍后重試" })
          res.on("end", () => {
            if (fs.statSync(tmpNWPath).size < 10) return reject({ message: "更新包出錯(cuò),請(qǐng)稍后重試" })
            fs.renameSync(tmpNWPath, appPath)
            resolve(json)
          })
          res.pipe(packageZip)
        })
        .on("error", reject)
    })
  })
}

// 重啟自己
export function restartSelf(waitTime) {
  setTimeout(() => {
    require("child_process").spawn("restart.bat", [], { detached: true, cwd: rootPath })
  }, ~~waitTime || 2000)
}

這兒有個(gè)小小的 hack ,仔細(xì)看看代碼的同學(xué)應(yīng)該已經(jīng)發(fā)現(xiàn)了 restart.bat 。我嘗試了很多辦法,想讓 NW.exe 重啟自己,最終多番嘗試后失敗了。。。就寫(xiě)了個(gè) bat 來(lái)重啟自己。

taskkill /im doudou.exe /f
start .doudou.exe
exit

如果有其他更好的辦法,歡迎踴躍來(lái)提 issue 或者直接私信我,謝謝!

可能會(huì)有同學(xué)會(huì)問(wèn),為啥不直接下載 exe 包下來(lái),再打開(kāi)引導(dǎo)安裝?
我試過(guò)了,當(dāng)應(yīng)用被安裝在 C:Program Files 目錄里面,管理員權(quán)限都不能寫(xiě) .exe 后綴的文件進(jìn)去。。。所以,我干脆用瀏覽器打開(kāi)我們的應(yīng)用的下載頁(yè),讓用戶自己去下載后,自己安裝算了。這兒應(yīng)該可以優(yōu)化,下載到 用戶數(shù)據(jù)目錄,或者其他臨時(shí)目錄。

4、update 頁(yè)面

這個(gè)頁(yè)面就沒(méi)啥技術(shù)點(diǎn),就是體力勞動(dòng)了。根據(jù)前面 getUpdateJson 方法獲得的 json 來(lái)渲染出要更新的版本和更新信息,然后提供一個(gè)更新按鈕,按鈕點(diǎn)擊后,執(zhí)行 updatePackage 這個(gè)方法,如果順利執(zhí)行就在 then 里面調(diào)用 restartSelf 重啟自己就行了。

整體效果就是這樣的

如果對(duì)您有用,幫我點(diǎn)個(gè) star ,謝謝!您的支持是我繼續(xù)更新下去的動(dòng)力。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/88195.html

相關(guān)文章

  • vue2 webpack 快速建構(gòu) NW.js 項(xiàng)目(3)

    摘要:增加文件這個(gè)文件主要做的事情就是整理出用的,然后再調(diào)用進(jìn)行打包在中增加打包入口增加下面這一行代碼在打包完成的回調(diào)中簡(jiǎn)單部就完成了打包,是不是異常清晰和簡(jiǎn)單。參見(jiàn)如果有就寫(xiě)上新增這個(gè)文件就是用來(lái)打包下安裝包的。。。 閱讀本文需要一點(diǎn) JS 基礎(chǔ)和閱讀的耐心,我特么自己寫(xiě)完后發(fā)現(xiàn)這文章咋這么長(zhǎng)啊。。。如果你認(rèn)真看完算我輸! 另我專門(mén)做了個(gè) vue-nw-seed 項(xiàng)目,里面包含了我這篇...

    史占廣 評(píng)論0 收藏0
  • 關(guān)于Vue2一些值得推薦的文章 -- 五、六月份

    摘要:五六月份推薦集合查看最新的請(qǐng)點(diǎn)擊集前端最近很火的框架資源定時(shí)更新,歡迎一下。蘇幕遮燎沈香宋周邦彥燎沈香,消溽暑。鳥(niǎo)雀呼晴,侵曉窺檐語(yǔ)。葉上初陽(yáng)乾宿雨,水面清圓,一一風(fēng)荷舉。家住吳門(mén),久作長(zhǎng)安旅。五月漁郎相憶否。小楫輕舟,夢(mèng)入芙蓉浦。 五、六月份推薦集合 查看github最新的Vue weekly;請(qǐng)::點(diǎn)擊::集web前端最近很火的vue2框架資源;定時(shí)更新,歡迎 Star 一下。 蘇...

    sutaking 評(píng)論0 收藏0
  • 關(guān)于Vue2一些值得推薦的文章 -- 五、六月份

    摘要:五六月份推薦集合查看最新的請(qǐng)點(diǎn)擊集前端最近很火的框架資源定時(shí)更新,歡迎一下。蘇幕遮燎沈香宋周邦彥燎沈香,消溽暑。鳥(niǎo)雀呼晴,侵曉窺檐語(yǔ)。葉上初陽(yáng)乾宿雨,水面清圓,一一風(fēng)荷舉。家住吳門(mén),久作長(zhǎng)安旅。五月漁郎相憶否。小楫輕舟,夢(mèng)入芙蓉浦。 五、六月份推薦集合 查看github最新的Vue weekly;請(qǐng)::點(diǎn)擊::集web前端最近很火的vue2框架資源;定時(shí)更新,歡迎 Star 一下。 蘇...

    khs1994 評(píng)論0 收藏0
  • 使Vue2+webpack+Es6快速開(kāi)發(fā)一個(gè)移動(dòng)端項(xiàng)目,封裝屬于自己的jsonpAPI手勢(shì)響應(yīng)

    摘要:導(dǎo)語(yǔ)最近看到不少使用制作的音樂(lè)播放器,挺好玩的,本來(lái)工作中也經(jīng)常使用,一起交流學(xué)習(xí),好的話點(diǎn)個(gè)哦本項(xiàng)目特點(diǎn)如下原生封裝自己的跨域請(qǐng)求函數(shù),支持調(diào)用,支持錯(cuò)誤處理制作一些復(fù)用性強(qiáng)的組件,如輪播圖組件,支持手勢(shì)滑動(dòng),無(wú)限循環(huán),圖片按需加載 showImg(http://upload-images.jianshu.io/upload_images/4149586-1849aa83e2decf...

    lemanli 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<