亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 開發 > JS > 正文

webpack-mvc 傳統多頁面組件化開發詳解

2024-05-06 16:50:36
字體:
來源:轉載
供稿:網友

最近有一個項目,還是使用的傳統 MVC 模式開發,完全基于jQuery,使用了基于java模板引擎velocity,頁面中嵌入了大量java語法,使得前后端分離不徹底,工程打包上線苦不堪言,為實現后端為服務化,前端也得徹底從后端中分離出來。

方案: webpack4 + ejs

webpack

  • 打包所有的 資源
  • 打包所以的 腳本
  • 打包所以的 圖片
  • 打包所以的 樣式
  • 打包所以的 表

ejs

高效的 JavaScript 模板引擎,代替 velocity

webpack 配置

基本插件

  • @babel/core,@babel/preset-env,babel-loader

es6 語法轉譯

  • css-loader,style-loader

編譯打包css

  • node-sass,sass-loader

解析sass

  • postcss-loader,autoprefixer

自動給樣式增加瀏覽器前綴

  • mini-css-extract-plugin

將css從js中抽離出來為單獨文件

  • optimize-css-assets-webpack-plugin

壓縮css

  • uglifyjs-webpack-plugin

壓縮js

  • ejs-loader

解析ejs模板文件

  • html-webpack-plugin

生成html文件

  • rimraf

刪除文件、文件夾

  • watch

監聽文件變化

上面是一些要用的插件,具體用法不累述。

入口文件

入口文件長這樣(可單一入口,也可多入口):

// 多入口entry: { pageA: './src/pageA/index.js', pageB: './src/pageB/index.js', 'pageC/login': './src/pageC/login/login.js'}

出口文件:

output: { filename: '[name].js', path: path.resolve(__dirname, '../dist'),}

filename 值中的 [name] 對應入文件的 key 值,/ 分割文件夾。 

最后就會在dist文件夾下生產文件:

  • dist/pageA/index.js
  • dist/pageB/index.js
  • dist/pageC/login/login.js

既然是多頁面開發,就要有多個入口,每個頁面都要有自己對應的js入口,這樣我們只需要遍歷html文件,然后找到對應的js,處理成 entry 對象即可

const path = require('path')const glob = require('glob')const pages = (entries => { let entry = {}, htmlArr = [] // 格式化生成入口 entries.forEach((file) => {  // ...../webpack-mvc/src/page/pageA/index.html  const fileSplit = file.split('/')  const length = fileSplit.length  // 頁面入口 pageA/index.html  const filePath = fileSplit.slice(length - 2, length).join('/')   // 根據html路徑找到對應的js路徑,js可以和html放在同一文件夾,也可單獨放在一個文件夾內,只要能找到   const jsPath = path.resolve(__dirname, `../src/page/${filePath.split('.')[0]}.js`)   // _main.ejs 頁面主題框架,html組件化  pageHtml = path.resolve(__dirname, '../src/_main.ejs')   if (!fs.existsSync(jsPath)) {   return;  }  entry['js/' + filePath.split('.')[0]] = jsPath // 加 js/ 即表示將打包后的js單獨放在一個文件夾內 }) return entry})(glob(path.resolve(__dirname, '../src/page/*/*.html'), {sync: true}))

上面只是本例的目錄結構,根據不同的目錄結構,更改路徑即可,目的就是得到 ‘js打包生成路徑': ‘入口js' 映射關系。

html(ejs) 組件化

頁面框架

1、主體框架 src/_main.ejs

<!DOCTYPE html><html lang="en"><head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width,initial-scale=1.0"> <title><%= htmlWebpackPlugin.options.title %></title></head><body> <div class="main-head">  <%= require('@/common/components/header/header.ejs')() %> </div> <div class="main-content">  <%= htmlWebpackPlugin.options.content %> </div> <div class="main-foot">  <%= require('@/common/components/footer/footer.ejs')() %> </div></body></html>

2、公共頁面 

header、footer每個頁面都包含,所以放入主體框架頁面內 

3、頁面各自部分 

各個頁面只需要寫自己頁面的html內容即可,并且還可以引入公共組件ejs

// pageA/index.html<div> <h1>pageA index</h1></div>// pageA/login.html<div> <%= require('@/common/components/form.ejs')() %> <h1>pageA login</h1></div>

網上查了很多資料,沒找到可以實現上面步驟的方法,基本都是要在每個頁面的js里去寫一些ejs語法,做不到我想要的只關注此頁面本身的內容。

替換 _main.ejs,生成臨時模板

我的解決方法是 通過 node 讀取頁面 html 文件,然后替換 _main.ejs 中的 content 部分,生成一個臨時 ejs 模板文件,然后通過插件 html-webpack-plugin 生成最終頁面 html 文件

function createTemplate(file, jsPath, entry) { let obj = {  title: '',  template: '',  filename: '',  chunks: [jsPath] } // _main.ejs 頁面主題框架,html組件化 let mainHtml = path.resolve(__dirname, '../src/_main.ejs') let fileSplit = file.split('/') // html 生成路徑 let filename = fileSplit.slice(fileSplit.length - 2).join('/').split('.')[0]; let strContent = fs.readFileSync(file, 'utf-8') let strMain = fs.readFileSync(mainHtml, 'utf-8') let template = fileSplit.slice(fileSplit.length - 2).join('_').split('.')[0]; strMain = strMain.replace(/<%= htmlWebpackPlugin.options.content %>/, strContent) fs.writeFileSync(path.resolve(__dirname, `../src/template/template_${template}.ejs`), strMain) obj.template = path.resolve(__dirname, `../src/template/template_${template}.ejs`) obj.filename = filename return obj}

有了上面方法的思路,我們可以在各自頁面中做更多的操作

頁面 title

// pageA/index.html<%=title 頁面A %><div> <h1>pageA index</h1></div>

頁面直接引入js,只壓縮不打包

// pageA/index.html<%=title 頁面A %><div> <h1>pageA index</h1></div><script src="js/common/util.js"></script><script src="js/common/server.api.js"></script>

這里引入js的路徑是最終文件壓縮生成的位置(dist目錄下),因為開發模式和生產環境路徑有所不同,所以等下在代碼中要區別不同環境去替換不同的路徑。

頁面引入ejs組件

// pageA/index.html<%=title 頁面A %><div> <%= require('@/common/components/form.ejs')() %> <h1>pageA index</h1></div><script src="js/common/util.js"></script><script src="js/common/server.api.js"></script>

page.config.js

const fs = require('fs')const path = require('path')const glob = require('glob')if (process.env.NODE_ENV === 'development') { const rimraf = require('rimraf') rimraf.sync(path.resolve(__dirname, '../src/template/*'), fs, function cb() {  console.log('template目錄已清空') })}const pages = (entries => { let entry = {}, htmlArr = [] // 格式化生成入口 entries.forEach((file) => {  // ...../webpack-mvc/src/page/pageA/index.html  let fileSplit = file.split('/')  let length = fileSplit.length  // 頁面入口 page/pageA/index.html  let filePath = fileSplit.slice(length - 3, length).join('/')  // 根據html路徑找到對應的js路徑,js可以和html放在同一文件夾,也可單獨放在一個文件夾內,只要能找到  let jsFile = path.resolve(__dirname, `../src/${filePath.split('.')[0]}.js`)  if (!fs.existsSync(jsFile)) {   return;  }  let jsPath = 'js/' + filePath.split('.')[0]  entry['js/' + filePath.split('.')[0]] = jsFile  htmlArr.push(createTemplate(file, jsPath, entry)) }) return {entry, htmlArr}})(glob(path.resolve(__dirname, '../src/page/*/*.html'), {sync: true}))function scriptLinkEntry(entry, file) { // file: /js/common/js/util.js let fileNew = './src/' + file.split('/').slice(2).join('/') let fileSplit = fileNew.split('/') entry['js/common/' + fileSplit.slice(fileSplit.length - 1).join('/').replace('.js', '')] = fileNew}function replaceScript(content, entry) { let scriptLink = content.match(/<script.*src=["|'](.*)["|']><//script>/g) if (scriptLink) {  scriptLink.forEach(item => {   // src: /js/common/js/util.js   let src = item.match(/src=["|'](.*)["|']/)[1];   scriptLinkEntry(entry, src)   let scriptlinNew = src   // 生產環境根據頁面路徑找到js的相對路徑,開發環境 /js/ 指向 dist 目錄下 js 文件夾   if (process.env.NODE_ENV === 'production') {    let srcSplit = src.split('/')    srcSplit.splice(3, 1) // ['', 'js', 'common', 'util.js']    scriptLinkNew = `..${srcSplit.join('/')}` // ../js/common/util.js   }   content = content.replace(src, scriptLinkNew)   }) } return content;}function createTemplate(file, jsPath, entry) { let obj = {  title: '',  template: '',  filename: '',  chunks: [jsPath] } // _main.ejs 頁面主題框架,html組件化 let mainHtml = path.resolve(__dirname, '../src/_main.ejs') let fileSplit = file.split('/') // html 生成路徑 let filename = fileSplit.slice(fileSplit.length - 2).join('/').split('.')[0]; let strContent = fs.readFileSync(file, 'utf-8') let strMain = fs.readFileSync(mainHtml, 'utf-8') let template = fileSplit.slice(fileSplit.length - 2).join('_').split('.')[0] // 提取頁面title let titleMatch = strContent.match(/<%=title(.*)%>/) let if (titleMatch) {  strContent = strContent.replace(/<%=title(.*)%>/, '') } // 提取頁面與主體框架中引入的靜態js文件,將其放入入口文件中經行壓縮,并適應開發與生產路徑 strMain = replaceScript(strMain, entry) strContent = replaceScript(strContent, entry) strMain = strMain.replace(/<%= htmlWebpackPlugin.options.content %>/, strContent) fs.writeFileSync(path.resolve(__dirname, `../src/template/template_${template}.ejs`), strMain) obj.title = title obj.template = path.resolve(__dirname, `../src/template/template_${template}.ejs`) obj.filename = filename return obj}module.exports = pages;

熱刷新

此時熱刷新只能監聽到js和css的改變,因為模板是動態生成的,更改頁面內容時模板并沒有改變,所以無法觸發devServer的熱刷新,手動刷新也不會有變化,因為臨時模板文件沒有改變,借用插件 watch 來監聽html文件變化,然后重寫模板文件可解決問題。

const fs = require('fs')const path = require('path')const watch = require('watch')const { replaceScript } = require('./page.config.js')watch.watchTree(path.resolve(__dirname, '../src/page'), (f, curr, prev) => { if (typeof f == 'object' && prev === null && curr === null) {  // Finished walking the tree } else if (prev === null) {  // f is a new file  createTemplate(f) } else if (curr.link === 0) {  // f was removed } else {  createTemplate(f) }})function createTemplate(file) { if (file.indexOf('.html') === -1) {  return } console.log('file', file) let mainHtml = path.resolve(__dirname, '../src/_main.ejs') let strContent = fs.readFileSync(file, 'utf-8') let strMain = fs.readFileSync(mainHtml, 'utf-8') let template = file.split('//').slice(file.split('//').length - 2).join('_').split('.')[0] // 提取頁面與主體框架中引入的靜態js文件,將其放入入口文件中經行壓縮,并適應開發與生產路徑 // 這里不再處理 title 和 靜態js 入口壓縮 strMain = replaceScript(strMain, {}, true) strContent = replaceScript(strContent, {}, true) strContent = strContent.replace(/<%=(.*)%>/, '') strMain = strMain.replace(/<%= htmlWebpackPlugin.options.content %>/, strContent) fs.writeFileSync(path.resolve(__dirname, `../src/template/template_${template}.ejs`), strMain)}

這里不再處理title和靜態js入口壓縮,更改了這些只能再重新 npm run dev

國際化

const languageProperty = require('../properties/language.properties.js')function getLanText(val) { let lan = 'zh' // $.cookie('lan') let str = languageProperty[val] && languageProperty[val][lan] || val let defaultOpt = languageProperty[val] && languageProperty[val]['default'] let opts = defaultOpt && $.extend(true, [], defaultOpt) opts ? opts.unshift('') : false let args = opts && arguments.length === 1 ? opts : arguments if (args.length > 1) {  let params = Array.property.slice.call(args, 1)  return str.replace(/{(/d+)}/g, function(curr, index) {   return params[index]  }) } else {  return str }}function translateAll() { let num = $('html').find('[lang]').length let count = 0 if (num === 0) {  $('body').show() } $('html').find('[lang]').each(function() {  count += 1;  let lang = $(this).attr('lang')  if (lang === '') {   return;  }  let nodeName = $(this)[0].nodeName  let text = getLanText(lang)  // 簡單處理,復雜的可再這里更改  if (nodeName === 'INPUT') {   $(this).attr('placeholder', text)  } else {   $(this).html(text)  }  if (count === num) {   $('body').show()  } })}module.exports = { getLanText, translateAll }

在header.js里調用一次就可以了。

結語

至此,傳統多頁面組件化開發流程基本完成,可以完全脫離后臺愉快的開發前端了,拋棄eclipse,擁抱vsCode。 

此文只構建了基本的框架,中間還有很多優化點,打包速度,公共代碼等等都沒有去細究,等頁面、代碼量增加,這也是必須去研究的,路漫漫其修遠兮。

Guthub 可直接 npm run dev, npm run build 運行, 順便求個Star
注:相關教程知識閱讀請移步到JavaScript/Ajax教程頻道。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产精品成人国产乱一区| 日韩动漫免费观看电视剧高清| 久久91亚洲人成电影网站| 97精品国产97久久久久久春色| 国产欧美精品日韩精品| 亚洲图片在线综合| 欧美在线免费视频| 国产精品高潮在线| 成人福利网站在线观看11| 在线播放日韩欧美| 亚洲aⅴ男人的天堂在线观看| 久久全球大尺度高清视频| 欧美激情第三页| 亚洲精品自拍偷拍| 久久人人爽人人爽人人片av高请| 久久久久久亚洲精品| 国产精品一区二区av影院萌芽| 亚洲免费视频网站| 日韩成人av网址| 亚洲无线码在线一区观看| 日韩麻豆第一页| 久久精品中文字幕一区| 日韩精品在线视频观看| 国产美女直播视频一区| 性欧美办公室18xxxxhd| 热99精品只有里视频精品| 久久在线免费视频| 久久久噜久噜久久综合| 亚洲精品视频在线观看视频| 国产精品一区二区三区免费视频| 成人欧美在线视频| 国产中文欧美精品| 国产欧美一区二区三区在线看| 日韩毛片中文字幕| 色综合久久天天综线观看| 日韩高清av一区二区三区| 欧美超级乱淫片喷水| 国产噜噜噜噜噜久久久久久久久| 亚洲视频视频在线| 国产精品久久久久久久久久ktv| 91亚洲永久免费精品| 美日韩丰满少妇在线观看| 欧美又大又硬又粗bbbbb| 亚洲xxxxx电影| 成人有码在线播放| 欧美性videos高清精品| 97在线视频免费| 欧美xxxx18性欧美| 国产精品高潮呻吟视频| 成人激情视频在线| 国内精品视频在线| 色偷偷偷亚洲综合网另类| 欧美精品日韩三级| 久久久精品一区二区| 奇门遁甲1982国语版免费观看高清| 国模精品一区二区三区色天香| 久久人人看视频| 精品色蜜蜜精品视频在线观看| 北条麻妃一区二区三区中文字幕| 国产成人久久久精品一区| 尤物九九久久国产精品的特点| 国产成人精品av| 久久中文精品视频| 亚洲欧洲日产国码av系列天堂| 日韩高清有码在线| 欧美日本在线视频中文字字幕| 中文字幕精品视频| 日本在线精品视频| 国产精品久久色| 欧美电影免费观看高清| 久久九九全国免费精品观看| 91av在线播放视频| 国产成人av网址| 日本免费久久高清视频| 亚洲肉体裸体xxxx137| 国产成人一区二区| 奇米影视亚洲狠狠色| 日韩美女免费视频| 日韩激情av在线免费观看| 国产精品中文在线| 精品国产一区二区三区在线观看| 久久综合免费视频| 亚洲第一精品福利| 日韩欧美成人网| 国产精品视频在线观看| 国产成人精品亚洲精品| 久久免费福利视频| 视频直播国产精品| 中文字幕精品—区二区| 日韩精品在线免费播放| 国内精品小视频在线观看| 国产精品高精视频免费| 欧美日韩中文字幕在线| 国产精品视频导航| 黑人巨大精品欧美一区二区| 日韩中文字幕国产精品| 91国内免费在线视频| 一本色道久久88亚洲综合88| 亚洲国产成人精品久久久国产成人一区| 国产精品福利无圣光在线一区| 欧美成人午夜剧场免费观看| 日本午夜在线亚洲.国产| 亚洲精品国产成人| 久久久久久av| 日韩中文视频免费在线观看| 久久人人爽人人爽人人片av高清| 欧美激情视频网| 成人在线视频网站| 国外日韩电影在线观看| 国产综合香蕉五月婷在线| 欧美国产乱视频| 欧美一区二区三区精品电影| 91国偷自产一区二区三区的观看方式| 青青久久aⅴ北条麻妃| 精品国产一区二区三区久久狼5月| 亚洲欧美在线免费观看| 成人在线精品视频| 精品久久久久国产| 国产日韩精品一区二区| 亚洲国产精品视频在线观看| 欧美在线视频一区二区| 国产精品嫩草视频| 欧美视频在线视频| 久久久久久综合网天天| 91丝袜美腿美女视频网站| 欧美黑人一级爽快片淫片高清| 精品久久久久久久久久国产| 欧美激情国内偷拍| 国内外成人免费激情在线视频| 久久久av网站| 午夜精品久久久久久久白皮肤| 国产精品一区二区久久精品| 亚洲欧洲在线播放| 亚洲视频在线免费观看| 久热99视频在线观看| 国产精品国产三级国产aⅴ9色| 国产精品入口福利| 黑人与娇小精品av专区| 国产精品视频999| 日韩久久免费视频| 日韩精品在线观| 欧美成人激情视频免费观看| 国产精品电影观看| 欧美成人免费观看| 久久在线免费视频| 韩国国内大量揄拍精品视频| 久久免费视频网| 国产精品综合不卡av| 中文字幕亚洲二区| 欧美视频在线观看 亚洲欧| 欧美专区中文字幕| 国产精品网红福利| 久久久久久香蕉网| 色综合91久久精品中文字幕| 久热在线中文字幕色999舞| 亚洲高清av在线| 国产精品男人爽免费视频1| 国产在线高清精品| 国产精品久久91| 97在线免费观看| 久久青草福利网站| 国产精品一区二区三区免费视频| xxx欧美精品| 欧美性xxxxxx|