Резюме исследования: измерьте эффект обучения тем, сможете ли вы построить колесо. В последнее время я делаю страницы активности (+++) o(╥﹏╥)o
[Vue Multi-Page Project H5] Active Page Templates Portalgithub
задний план
В большинстве случаев мы используем веб-пакет для упаковки одностраничных приложений.На данный момент нам нужно настроить только одну запись и один файл шаблона, но это не тот случай.Иногда мы также сталкиваемся с многостраничными проектами, такими как некоторые проекты с относительно длительным жизненным циклом.Краткая страница активности:
Требования к странице активности
Ситуация, с которой часто сталкивается мобильный h5, заключается в том, что есть много действий и быстрых итераций. Но одностраничная ситуация явно неприменима, что в основном отражается в следующих аспектах:
- Многократно собирать проекты и добавлять их вручную 😭, что неэффективно.
--project1
---node_modules
---src
----style
----common
----components
----views
---package.json
--project2
---node_modules
---src
----style
----common
----components
----views
- Наша страница активности h5 имеет много общего:
- адаптация страницы
- Большое количество базовых компонентов (таких как: загрузка, диалог, кнопка, изготовление постера и т.д.)
- Большинство тем одинаковы
- Онлайн-развертывание новой страницы активности требует повторной настройки jenkins, что неэффективно.
Исходя из вышеуказанного статус-кво, преимущества многостраничности проявляются именно в это время.Поговорим об этом.После многостраничной интеграцииvue-multi-page
Эффект
vue-многостраничная инструкция по использованию
Многостраничная конфигурация активной страницы h5 в сочетании сjenkins
а такжеnginx
Для этого каждый раз, когда вы добавляете страницу, вам нужно толькоjenkins
Добавьте параметр к динамическим параметрам
Структура проекта:
1. Создайте свою страницу под страницами
--pages
---demo
----index.html
----app.vue
----main.js
Строить вручную? Невозможно, поэтому написал скрипт (по сути он для реализации функции копирования и копирования папок)
добавить в скрипт"new:page": "node scripts/createPage.js"
const fs = require('fs-extra')
const path = require('path')
const log = require('../utils/log')
log.info('【请输入页面名称,例如:pageDemo】')
process.stdin.on('data', async chunk => {
const inputName = String(chunk).replace(/\s*/g, '')
const pageTarget = path.resolve(__dirname, '../', 'src/pages', inputName)
const pageSource = path.resolve(__dirname, '../', 'pageTemplate')
if(!fs.existsSync(pageTarget)) {
log.info(`【sourceUrl】${pageSource}`)
log.info(`【targeturl】${pageTarget}`)
copyPublicFolder(pageSource, pageTarget)
}else{
log.err('【页面已存在,请重新创建】')
}
process.stdin.emit('end')
})
process.stdin.on('end', () => {
process.exit()
})
/**
* 复制文件夹
* @param {*string} source
* @param {*string} target
*/
function copyPublicFolder(source, target) {
try {
fs.copySync(source, target)
log.succes('【页面创建成功】')
} catch (err) {
log.error('【页面创建失败】')
}
}
Введите команду для извлечения из шаблона:
2. инициация проекта и упаковка
npm install
npm run serve --page=pages/demo
npm run build --page=pages/demo
npm run lint --page=pages/demo
доступ к разработке
npm run serve --page=pages/demo
http://localhost:8888/demo.html#/home
3. Сотрудничество с nginx
nginx нужно только настроить корневой каталог местоположения.
root
указать на упакованныйdist
Только что
location / {
root xxx/dist;
index index.html;
}
Таким образом, чтобы добавить страницу активности, вам нужно всего лишьpages
В новом каталоге вы можете напрямую посетить:
`http://www.bai.cn/page1/#/home`
`http://www.bai..cn/page2/#/home`
Если используется непрерывная интеграцияjenkins
, вы также можете использоватьjenkins
изChoice Parameter
- Choice Parameterдобавить параметры
page1
page2
- При построении вы можете напрямую выбрать соответствующую страницу.
3. Адрес доступа
http://www.bai.cn/page1/#/home
http://www.bai.cn/page2/#/home
4. Встроенные переменные среды
логотип | описывать |
---|---|
DEV | среда разработки |
TEST | тестовая среда |
PRE | предварительная среда |
PROD | Производственная среда |
5. Справочная ссылка Vue-CLI
vue-многостраничные инструкции по настройке
1. входная конфигурация входа
vue-cli
изpage
Атрибут позволяет нам определить несколько файлов входа, и с помощью этого атрибута реализуется интеграция нескольких страниц активной страницы.
Каждая страница представляет собой папку в каталоге src/. В этой папке есть два подкаталога, в которых хранится шаблон html страницы, файл стиля css и файл ввода index.js.
Так как есть правила, то программирование однозначно возможно.Согласно этому правилу, каждая страница представляет собой каталог в ./src, имя каталога - это имя страницы, и структура в этом каталоге одинаковая. , то можно получить все названия страниц (например, trade, demo) с помощью универсального метода. Пример этого универсального метода выглядит следующим образом:
const glob = require('glob')
const path = require('path')
/**
* @param {*String} filterPath
* @param {*String} filterStr
*/
function getEntry (filterPath, filterStr) {
let globPath = filterPath
let files = glob.sync(globPath)
let dirname, entries = {}
for (let i = 0; i < files.length; i++) {
dirname = path.dirname(files[i])
if (dirname.includes(filterStr)) {
entries['index'] = {
entry: dirname + '/main.js',
template: dirname + '/index.html'
}
break
}
}
console.log('getEntry:', entries)
return entries
}
// 输入
getEntry('src/pages/**/*.html', getNPMParams().page)
// 输出
{
index: {
entry: 'src/pages/demo/main.js',
template: 'src/pages/demo/index.html'
}
}
с помощьюglobЭта библиотека, проходящая через каталог .src/, имеет это правилоsrc/pages/**/*.html
подкаталог, имя этого подкаталога получается путем обычного сопоставления.
Чтобы получить все имя страницы, ниже проще справиться.
Далее мы собираемся упаковатьpages/demo
настраиваетсяnpm
Реализация параметра команды:
npm run serve --page=pages/demo
npm run build --page=pages/demo
npm run lint --page=pages/demo
Таким образом, простоprocess.argv
понятноnpm
Параметры командной строки, вы можете узнать, какая страница в данный момент должна быть запущена или построена.
Конкретный метод приобретения выглядит следующим образом:
function getNPMParams() {
let argv
try {
argv = JSON.parse(process.env.npm_config_argv).original
} catch (ex) {
argv = process.argv
}
console.log('argv----', argv)
const params = {}
argv &&
argv.forEach(item => {
const arr = item.split(/=/gi)
if (item.slice(0, 2) === '--' && arr.length === 2) {
params[arr[0].slice(2)] = arr[1]
}
})
if (params && params.page) {
if (!fs.existsSync(path.resolve(__dirname, '../src/', params.page))) {
console.log(`${params.page}不存在,请检查pages下是否有该目录`)
process.exit()
}
} else {
console.log('输入格式请参考:npm run serve --page=pages/xxxx')
process.exit()
}
return params
}
vue.config.js
Конкретная конфигурация
const { getEntry, getNPMParams } = require('./webpack/utils')
const entry = getEntry('src/pages/**/*.html', getNPMParams().page)
const IS_PRODUCTION = process.env.ENV === 'prod'
const port = 8888
const pageName = getNPMParams().page.split('/')[1]
module.exports = {
publicPath: './',
lintOnSave: !IS_PRODUCTION,
// 根据入口构建
pages: entry,
// 自定义输出
outputDir: 'dist/' + pageName,
devServer: {
port: port,
disableHostCheck: true
// compress: true // GZIP
}
}
2. Конфигурация среды
фактическиvue-cli
Конфигурация схемы и окружения предоставлена
Конкретно, потому что,vue-cli
Предоставленный режим и конфигурация среды распределены в разных файлах env в соответствии с разными средами, что неудобно для сравнения и ссылки.
Итак, этот интеграционный проект используетcross-env
а такжеDefinePlugin
, настройте среду, внедрив переменные среды. Будет только один, когда закончитеenv.js
Конфигурация:
const localConfig = {
DEV: {
ENV: 'dev',
BASE_API: '',
},
TEST: {
ENV: 'test',
BASE_API: '',
},
PRE: {
ENV: 'pre',
BASE_API: '',
},
PROD: {
ENV: 'prod',
BASE_API: '',
}
}
module.exports = (conf => {
const systemEnvs = ["DEV", "PROD", "TEST", "PRE"]
systemEnvs.forEach(env => {
conf[env] = Object.assign(
{
BASE_API: "/",
PROCESS_ENV: env.toLocaleLowerCase(),
NODE_ENV: "production"
},
conf[env] || {}
)
})
return conf
})(localConfig)
Далее, вvue.config.js
изchainWebpack
Вставить переменные окружения в
const path = require('path')
const { getEntry, getNPMParams } = require('./webpack/utils')
const entry = getEntry('src/pages/**/*.html', getNPMParams().page)
const IS_PRODUCTION = process.env.ENV === 'prod'
const ENV_CONFIG = require('./config/env')
const port = 8888
const pageName = getNPMParams().page.split('/')[1]
module.exports = {
publicPath: './',
lintOnSave: !IS_PRODUCTION,
// 根据入口构建
pages: entry,
// 自定义输出
outputDir: 'dist/' + pageName,
devServer: {
port: port,
},
chainWebpack: config => {
// 注入环境变量
config.plugin('define').tap(args => {
args[0]['process.env'] = JSON.stringify(ENV_CONFIG[(process.env.PROCESS_ENV).toLocaleUpperCase()])
return args
})
}
}
3. гибкая конфигурация
Адаптивная схема используетflexible
схема, конфигурация следующая:
module.exports = {
publicPath: './',
lintOnSave: !IS_PRODUCTION,
// 根据入口构建
pages: entry,
// 自定义输出
outputDir: 'dist/' + pageName,
devServer: {
port: port,
disableHostCheck: true
// compress: true // GZIP
},
css: {
loaderOptions: {
css: {},
postcss: {
plugins: [
require('postcss-pxtorem')({
rootValue: 75, // 换算的基数(设计图750的根字体为75)
selectorBlackList: ['.van'], // 要忽略的选择器并保留为px。
// 要忽略的选择器并保留为px。 selectorBlackList: ['.van'], // 要忽略的选择器并保留为px。
propList: ['*'], // 可以从px更改为rem的属性。
minPixelValue: 2 // 设置要替换的最小像素值。
})
]
}
}
}
}
4. Другая оптимизированная конфигурация
- Откажитесь от DLL, выберите
hard-source-webpack-plugin
отvue-cli
а такжеcreate-react-app
видно что не практичноdll
,Потому что:Webpack 4
Производительность упаковки достаточно хороша, и необходимо, чтобы dll продолжала поддерживаться.
Лучшая замена DLL, выбирайтеhard-source-webpack-plugin
chainWebpack: config => {
// 启用缓存
config.plugin('hardSource')
.use(new HardSourceWebpackPlugin())
}
- gzip-конфигурация,
terser
очистить консоль
const path = require('path')
const CompressionWebpackPlugin = require('compression-webpack-plugin')
const { getEntry, getNPMParams } = require('./webpack/utils')
const entry = getEntry('src/pages/**/*.html', getNPMParams().page)
const IS_PRODUCTION = process.env.ENV === 'prod'
const ENV_CONFIG = require('./config/env')
const port = 8888
const pageName = getNPMParams().page.split('/')[1]
module.exports = {
publicPath: './',
lintOnSave: !IS_PRODUCTION,
// 根据入口构建
pages: entry,
// 自定义输出
outputDir: 'dist/' + pageName,
devServer: {
port: port,
disableHostCheck: true
// compress: true // GZIP
},
chainWebpack: config => {
if (IS_PRODUCTION) {
// 开启gzip,需要配置nginx
config.plugin('compressionPlugin')
.use(new CompressionWebpackPlugin({
asset: '[path].gz[query]',
algorithm: 'gzip',
test: /\.(js|css|html|svg)$/,
threshold: 10240, // 大于10K才压缩gzip
minRatio: 0.8 // 压缩比例(minRatio = Compressed Size / Original Size)
}))
// 清除生产环境清除控制台输出
config.optimization.minimizer('terser').tap((args) => {
args[0].terserOptions.compress.drop_console = true
return args
})
}
},
// 生产环境关闭sourceMap
// productionSourceMap: !IS_PRODUCTION,
}
инструменты проекта
- Сквозной инструмент оценки
browser.js
. главным образом черезnavigator.userAgent
/* 判断浏览器类型 */
export const browser = {
versions: (function() {
var u = navigator.userAgent
return {
trident: u.indexOf('Trident') > -1, // IE内核
presto: u.indexOf('Presto') > -1, // opera内核
webKit: u.indexOf('AppleWebKit') > -1, // 苹果、谷歌内核
gecko: u.indexOf('Gecko') > -1 && u.indexOf('KHTML') === -1, // 火狐内核
mobile: !!u.match(/AppleWebKit.*Mobile.*/), // 是否为移动终端
ios: !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/), // ios终端
android: u.indexOf('Android') > -1 || u.indexOf('Adr') > -1, // android终端
iPhone: u.indexOf('iPhone') > -1, // 是否为iPhone或者QQHD浏览器
iPad: u.indexOf('iPad') > -1 || u.indexOf('Macintosh') > -1, // 是否iPad
webApp: u.indexOf('Safari') === -1, // 是否web应该程序,没有头部与底部
weixin: u.indexOf('MicroMessenger') > -1, // 是否微信 (2015-01-22新增)
qq: u.indexOf(' QQ') > -1 // 是否QQ
}
}()),
language: (navigator.browserLanguage || navigator.language).toLowerCase()
}
- Инструмент отложенной загрузки изображений
v-lazy
Для получения подробной информации см.:Компоненты - Ручной разрыв lazyLoad и v-lazy
// 引入默认图片
import loadingImg from '@/assets/loading.gif'
let timer = null
// 创建一个监听器
const observer = new IntersectionObserver((entries) => {
// entries是所有被监听对象的集合
entries.forEach(entry => {
// 当被监听元素到临界值且未加载图片时触发。
if (entry.isIntersecting || entry.intersectionRatio > 0) {
if (!entry.target.isLoaded) {
const lazyImage = entry.target
// 设置img的真实图片地址data-src
lazyImage.src = lazyImage.dataSrc
observer.unobserve(lazyImage)
}
}
})
})
export default {
// inserted时元素已经插入页面,能够直接获取到dom元素的位置信息
inserted(el, binding, vnode) {
clearTimeout(timer)
// 初始化时展示默认图片
el.src = loadingImg
// 将需要加载的图片地址绑定在dom上
el.dataSrc = binding.value
observer.observe(el)
// 在组件卸载的时候停止监听
const vm = vnode.context
timer = setTimeout(() => {
vm.$on('hook:beforeDestroy', () => {
observer.disconnect()
})
}, 20)
},
// 图片更新触发
update(el, binding) {
el.isLoaded = false
el.dataSrc = binding.value
}
}
Наконец - адрес проекта на github
[Многостраничный проект Vue h5] Портал шаблонов активных страницgithub
С тех пор: после завершения вышеописанной модификации, добавления активных страниц в проект в дальнейшем нужно только добавить соответствующую директорию под страницы.Используя инструменты, интегрированные jenkins, вам нужно только добавить параметры конфигурации, что, безусловно, один раз универсальный подход. .
PS: Добро пожаловать на обмен и изучение, пожалуйста, укажите на недостатки.