我們是如何從ng1遷移ing到vue的

我們是如何從ng1遷移ing到vue的

原本的技術(shù)棧 ng1 + gulp + slim +
vue *2 + iframe 的一個(gè)后端管理項(xiàng)目

這是一個(gè)本身因?yàn)槿耸植蛔悖婚_始由后端同學(xué)創(chuàng)建的后端管理項(xiàng)目,基本采用了gulp + ng1來進(jìn)行開發(fā),同時(shí)前端接手之后為了方便開發(fā)以及跟上潮流,采用了新開子目錄使用vue開發(fā),nginx和iframe進(jìn)行整合的方式,最后一個(gè)項(xiàng)目變成了三個(gè)項(xiàng)目,其實(shí)最開始進(jìn)行開發(fā)的時(shí)候,連怎么啟動(dòng)都不知道??

要解決的問題

項(xiàng)目的層級(jí)結(jié)構(gòu)

原本的結(jié)構(gòu)

頂級(jí)目錄只包含多個(gè)子文件夾以及build.sh,每個(gè)子項(xiàng)目需要獨(dú)立進(jìn)行編譯以及開發(fā)

新的結(jié)構(gòu)

采用signle-spa作為入口文件解決方案,統(tǒng)一管理所有項(xiàng)目的入口文件,實(shí)現(xiàn)一次啟動(dòng),所有項(xiàng)目都能一起開發(fā)以及編譯,省去了來回切換以及端口沖突

構(gòu)建語言的混亂

原本的架構(gòu)

原本的app是使用ng1來進(jìn)行編寫js部分,slim來編寫頁面模版,同時(shí)使用gulp來完成遍歷所有的js文件,并打包到一個(gè)js中,后來一些新的頁面部分采用iframe引入另一個(gè)vue-cli項(xiàng)目,兩者之間通過cookie來進(jìn)行登錄數(shù)據(jù)的共享。

更新之后的架構(gòu)

因?yàn)槟0嫖募膯栴},仍然以gulp為主,webpack負(fù)責(zé)vue和原本app的js打包和資源文件的編譯工作,大家約定好,原本的ng部分盡量不更新,新的采用vue進(jìn)行編寫

逐步過渡

原本的方案

老的不管它,需要更新就回去更新,新的需求去vue的項(xiàng)目中編寫

現(xiàn)在的方案

single-spa進(jìn)行頁面的拆分,將需要更新的老的ng部分作為一個(gè)新的子app,拆分出來之后再進(jìn)行更新,保證局部更新,不影響整體

解決過程

編譯工具

確定了整體的遷移方案之后,就是首先對(duì)編譯工具的改造了,最開始是想把gulp先替換成webpack的(因?yàn)榱?xí)慣配置webpack了,以及webpack4 + babel7真的編譯速度快了很多)

但是因?yàn)閟lim始終找不到適合使用的webpack插件的關(guān)系,最終決定還是保留gulp進(jìn)行編譯ng的相關(guān)的html文件

小問題

gulp支持webpack的問題

gulp-webpack插件支持的webpack版本是2,但是目標(biāo)是使用4(為了快),好在webpack支持使用node來進(jìn)行調(diào)用,只要在編譯結(jié)束之后給gulp一個(gè)回調(diào)就可以了


const webpack = require('webpack')
const fs = require('fs')

module.exports = function (webpackConfig) {
  return new Promise((resolve, reject) => {
    const compiler = webpack(webpackConfig);

    compiler.run((err, stats) => {
      if (err) {
        console.error(err)
        reject(err)
      }

      // 輸出
      process.stdout.write(stats.toString({
        // stats對(duì)象中保存著編譯過程中的各種消息
        colors: true, // 增加控制臺(tái)顏色開關(guān)
        modules: false, // 不增加內(nèi)置模塊信息
        children: false, // 不增加子級(jí)信息
        chunks: false, // 允許較少的輸出
        chunkModules: false // 不將內(nèi)置模塊的信息加到包信息
      }) + '\n\n')
    })

    compiler.hooks.afterEmit.tap('gulp', function() {
      resolve()
    })
  })
}

同理,devServer也使用自定義的腳本, 當(dāng)然因?yàn)楣驹颍渲械腶pi切換也直接放在devServer的before中

/**
 * webpack的devserver
 */
const webpack = require("webpack");
const WebpackDevServer = require("webpack-dev-server");
const proxy = require("http-proxy-middleware");

let env = "dev"; // 環(huán)境

module.exports = function(config) {
  return new Promise((resolve, reject) => {
    // node模式下需要進(jìn)行配置
    let devServerConfig = config.devServer;
    let devPath = `http://${devServerConfig.host}:${devServerConfig.port}/`;
    config.entry.app.unshift("webpack/hot/dev-server");
    config.entry.app.unshift(`webpack-dev-server/client?${devPath}`);

    const server = new WebpackDevServer(webpack(config), {
      open: devServerConfig.open,
      contentBase: config.output.path,
      publicPath: config.output.publicPath,
      hot: true,
      disableHostCheck: devServerConfig.disableHostCheck,
      historyApiFallback: true,
      inline: devServerConfig.inline,
      watchContentBase: devServerConfig.watchContentBase,
      before: function(app) {
        
      },
      stats: {
        colors: true
      }
    });

    server.listen(devServerConfig.port, devServerConfig.host, function(err) {
      if (err) {
        console.log(err);
        reject(err);
      }

      console.log(`Listening at ${devPath}`);
      resolve();
    });
  });
};


統(tǒng)一構(gòu)建和開發(fā)

因?yàn)槭褂玫恼Z言不一樣,負(fù)責(zé)的頁面不一樣,Store和Route也不一樣,想要統(tǒng)一的話,就必須解決這些問題

single-spa

SINGLE-SPA是一個(gè) JavaScript 元框架,它允許我們使用不同的框架構(gòu)建微前端,而這些框架可以共存于單個(gè)應(yīng)用中。


后端微服務(wù)架構(gòu)(microservice style)已經(jīng)流行了一段時(shí)間了,那么前端能不能同樣使用微服務(wù)呢?頁面拆分成不同的部分,互相之間互不干擾又緊密相連,single-spa正是為此而出現(xiàn)的,而且更好的事,single-spa支持幾乎所有的前端開發(fā)框架,是一個(gè)框架的元框架,而我們的遷移目標(biāo)正好就是將原本的ng部分拆分成一個(gè)個(gè)小的app然后逐步遷移到vue上么

統(tǒng)一Store

最終決定使用vuex作為整個(gè)項(xiàng)目的Store,但是如何與ng本身的rootScope進(jìn)行整合就又是一個(gè)問題了,好在通過import引入的js文件的作用域是共同的,只需要將ng的rootScope掛載上vuex,然后vuex使用插件的形式反過來觸發(fā)更新ng就行了.

import store from '../store'

Object.assign(store.state, {
    _ng: {
      $rootScope: $rootScope,
      $state: $state
    }
  })
/**
 * 同步這些數(shù)據(jù)到ng的Scope里
 * @param store
 */
export const ngPlugin = store => {
  // 當(dāng) store 初始化后調(diào)用
  store.subscribe(({ type, payload }, state) => {
    if (state._ng) {
      state._ng.$rootScope[type] = payload
      state._ng.$rootScope.$apply()
    }
  })
}

統(tǒng)一路由

目前仍然各自為政,只不過路由跳轉(zhuǎn)方式通過指令的方式,將vue和ng的跳轉(zhuǎn)修改為使用single-spa的路由跳轉(zhuǎn)方式. 當(dāng)然,因?yàn)橹皀g的路由是繼續(xù)hash而不是history,所以還有一部分兼容操作

import { navigateToUrl } from 'single-spa'

directives: {
      // spa鏈接
      spaLink (element, {value}) {
        element.style.display = 'inline-block'
        element.style.width = '100%'
        // 自動(dòng)判斷hash類型
        let hash = (value || '').includes('#') ? value.replace('/#', '#') : `#/${value}`
        element.setAttribute('href', value ? hash : 'javascript:void(0)')
        element.addEventListener('click', (e) => {
          e.preventDefault()
          if (value) {
            navigateToUrl(hash)
          }
        })
      }
    },

目錄結(jié)構(gòu)統(tǒng)一

使用webpack的一個(gè)好處就是可以使用alias的方式 引入vue的相關(guān)組件,將原先平級(jí)的目錄都移入src之后,只要修改alias,就能做到無縫遷移了

拆分app

沒什么好說的,按照業(yè)務(wù)相關(guān)性和優(yōu)先級(jí)拆分,使用single-spa進(jìn)行管理

目前進(jìn)度

當(dāng)然啦,遷移也不是一朝一夕的事,其實(shí)主要把整個(gè)項(xiàng)目的側(cè)邊欄導(dǎo)航欄給換了,支持了一直吵著要做的搜索框,更換的過程中還出過不少問題,不過因?yàn)椴鸱至薬pp,倒是沒有影響整個(gè)項(xiàng)目的正常運(yùn)行,看來遷移有望,起碼現(xiàn)在啟動(dòng)項(xiàng)目不用各種切分支了??,同時(shí)因?yàn)橥祽校€專門寫了seed系統(tǒng),通過定義數(shù)據(jù)結(jié)構(gòu)快速生成項(xiàng)目頁面,這也是后話了

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容