mirror of
https://github.com/tenrok/vue-meta.git
synced 2026-06-16 19:30:34 +03:00
feat: add poc vue-compiler
This commit is contained in:
@@ -7,15 +7,15 @@
|
||||
<body style="padding: 0 20px">
|
||||
<h1>Vue Meta Examples</h1>
|
||||
<ul>
|
||||
<li><a href="basic">Basic</a></li>
|
||||
<!-- li><a href="basic">Basic</a></li>
|
||||
<li><a href="basic-render">Basic Render</a></li>
|
||||
<li><a href="keep-alive">Keep alive</a></li>
|
||||
<li><a href="multiple-apps">Usage with multiple apps</a></li>
|
||||
<li><a href="multiple-apps">Usage with multiple apps</a></li -->
|
||||
<li><a href="ssr">SSR</a></li>
|
||||
<li><a href="vue-router">Usage with vue-router</a></li>
|
||||
<li><a href="vuex">Usage with vuex</a></li>
|
||||
<!-- li><a href="vuex">Usage with vuex</a></li>
|
||||
<li><a href="vuex-async">Usage with vuex + async actions</a></li>
|
||||
<li><a href="async-callback">Async Callback</a></li>
|
||||
<li><a href="async-callback">Async Callback</a></li -->
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
const {
|
||||
baseParse,
|
||||
transform,
|
||||
generate,
|
||||
processIf,
|
||||
getBaseTransformPreset,
|
||||
createObjectExpression,
|
||||
createObjectProperty,
|
||||
} = require('@vue/compiler-core')
|
||||
|
||||
const { parse } = require('@vue/compiler-dom')
|
||||
|
||||
function headTransform(node, context) {
|
||||
console.log('NODE', node)
|
||||
if (node.type === 1 /* NodeTypes.ELEMENT */) {
|
||||
return () => {
|
||||
if (!context.parent.codegenNode) {
|
||||
context.parent.codegenNode = createObjectExpression([])
|
||||
}
|
||||
|
||||
const options = context.parent.codegenNode
|
||||
const option = createObjectProperty(
|
||||
node.tag,
|
||||
node.children.length === 1 ? node.children[0] : 'null'
|
||||
)
|
||||
|
||||
//options.properties.push(option)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = function (source, map) {
|
||||
// TODO: add options
|
||||
const ast = parse(source)
|
||||
//console.log('AST', ast)
|
||||
|
||||
const [nodeTransforms, directiveTransforms] = getBaseTransformPreset({
|
||||
prefixIdentifiers: true
|
||||
})
|
||||
|
||||
transform(ast, {
|
||||
prefixIdentifiers: true,
|
||||
nodeTransforms: [
|
||||
...nodeTransforms,
|
||||
headTransform
|
||||
],
|
||||
directiveTransforms
|
||||
})
|
||||
|
||||
const result = generate(ast, { mode: 'module' })
|
||||
|
||||
console.log(result.code)
|
||||
|
||||
this.callback(null, `
|
||||
import { computed } from 'vue'
|
||||
|
||||
${result.code}
|
||||
export default function (component) {
|
||||
const setup = component.setup
|
||||
|
||||
component.setup = function (...args) {
|
||||
console.log(component)
|
||||
const __htmlMetaData = computed(() => {
|
||||
|
||||
})
|
||||
|
||||
return {
|
||||
...setup.apply(this, args),
|
||||
__htmlMetaData
|
||||
}
|
||||
}
|
||||
|
||||
}`, map)
|
||||
/**/
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
|
||||
export default {
|
||||
|
||||
}
|
||||
+18
-17
@@ -20,28 +20,29 @@
|
||||
},
|
||||
"homepage": "https://github.com/nuxt/vue-meta#readme",
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.6.0",
|
||||
"@babel/node": "^7.6.1",
|
||||
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
|
||||
"@babel/preset-env": "^7.6.0",
|
||||
"babel-loader": "^8.0.6",
|
||||
"@babel/core": "^7.9.0",
|
||||
"@babel/node": "^7.8.7",
|
||||
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
||||
"@babel/preset-env": "^7.9.0",
|
||||
"@vue/compiler-sfc": "^3.0.0-alpha.10",
|
||||
"@vue/server-renderer": "^3.0.0-alpha.10",
|
||||
"babel-loader": "^8.1.0",
|
||||
"babel-plugin-dynamic-import-node": "^2.3.0",
|
||||
"consola": "^2.10.1",
|
||||
"consola": "^2.11.3",
|
||||
"core-js": "3",
|
||||
"cross-env": "^5.2.1",
|
||||
"cross-env": "^7.0.2",
|
||||
"express": "^4.17.1",
|
||||
"express-urlrewrite": "^1.2.0",
|
||||
"fs-extra": "^8.1.0",
|
||||
"fs-extra": "^9.0.0",
|
||||
"lodash": "^4.17.15",
|
||||
"vue": "^2.6.10",
|
||||
"vue-loader": "^15.7.1",
|
||||
"vue-meta": "^2.2.2",
|
||||
"vue-router": "^3.1.3",
|
||||
"vue-server-renderer": "^2.6.10",
|
||||
"vue-template-compiler": "^2.6.10",
|
||||
"vuex": "^3.1.1",
|
||||
"webpack": "^4.39.3",
|
||||
"webpack-dev-server": "^3.8.0",
|
||||
"vue": "next",
|
||||
"vue-loader": "next",
|
||||
"vue-meta": "^2.3.3",
|
||||
"vue-router": "next",
|
||||
"vue-template-compiler": "^2.6.11",
|
||||
"vuex": "^3.1.3",
|
||||
"webpack": "^4.42.1",
|
||||
"webpack-dev-server": "^3.10.3",
|
||||
"webpackbar": "^4.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ const app = express()
|
||||
|
||||
app.use(webpackDevMiddleware(webpack(WebpackConfig), {
|
||||
publicPath: '/__build__/',
|
||||
writeToDisk: false,
|
||||
writeToDisk: true,
|
||||
stats: {
|
||||
colors: true,
|
||||
chunks: false
|
||||
|
||||
+13
-11
@@ -1,13 +1,13 @@
|
||||
import Vue from 'vue'
|
||||
import Router from 'vue-router'
|
||||
import { createSSRApp } from 'vue'
|
||||
import { createRouter, createMemoryHistory } from 'vue-router'
|
||||
import VueMeta from '../../'
|
||||
|
||||
Vue.use(Router)
|
||||
/*Vue.use(Router)
|
||||
Vue.use(VueMeta, {
|
||||
tagIDKeyName: 'hid'
|
||||
})
|
||||
})*/
|
||||
|
||||
export default function createApp () {
|
||||
export default function createMyApp () {
|
||||
const Home = {
|
||||
template: `<div>
|
||||
<router-link to="/about">About</router-link>
|
||||
@@ -54,16 +54,15 @@ export default function createApp () {
|
||||
}
|
||||
}
|
||||
|
||||
const router = new Router({
|
||||
mode: 'history',
|
||||
base: '/ssr',
|
||||
const router = createRouter({
|
||||
history: createMemoryHistory('/ssr'),
|
||||
routes: [
|
||||
{ path: '/', component: Home },
|
||||
{ path: '/about', component: About }
|
||||
]
|
||||
})
|
||||
|
||||
const app = new Vue({
|
||||
const app = createSSRApp({
|
||||
router,
|
||||
metaInfo () {
|
||||
return {
|
||||
@@ -145,13 +144,16 @@ export default function createApp () {
|
||||
|
||||
<router-view />
|
||||
</div>`
|
||||
|
||||
})
|
||||
|
||||
const { set } = app.$meta().addApp('custom')
|
||||
app.use(router)
|
||||
|
||||
/*const { set } = app.$meta().addApp('custom')
|
||||
set({
|
||||
bodyAttrs: { class: 'custom-app' },
|
||||
meta: [{ charset: 'utf-8' }]
|
||||
})
|
||||
})*/
|
||||
|
||||
return { app, router }
|
||||
}
|
||||
|
||||
@@ -3,4 +3,4 @@ import createApp from './App'
|
||||
window.users = []
|
||||
|
||||
const { app } = createApp()
|
||||
app.$mount('#app')
|
||||
app.mount('#app')
|
||||
|
||||
+27
-19
@@ -1,11 +1,9 @@
|
||||
import path from 'path'
|
||||
import fs from 'fs-extra'
|
||||
import template from 'lodash/template'
|
||||
import { createRenderer } from 'vue-server-renderer'
|
||||
const { renderToString } = require('@vue/server-renderer')
|
||||
import createApp from './App'
|
||||
|
||||
const renderer = createRenderer({ runInNewContext: false })
|
||||
|
||||
const templateFile = path.resolve(__dirname, 'app.template.html')
|
||||
const templateContent = fs.readFileSync(templateFile, { encoding: 'utf8' })
|
||||
|
||||
@@ -17,24 +15,34 @@ process.server = true
|
||||
export async function renderPage ({ url }) {
|
||||
const { app, router } = await createApp()
|
||||
|
||||
router.push(url.substr(4))
|
||||
await router.push(url.substr(4))
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
router.onReady(async () => {
|
||||
const matchedComponents = router.getMatchedComponents()
|
||||
// no matched routes, reject with 404
|
||||
if (!matchedComponents.length) {
|
||||
return reject({ code: 404 })
|
||||
}
|
||||
await router.isReady()
|
||||
/*console.log(router)
|
||||
const matchedComponents = router.getMatchedComponents()
|
||||
// no matched routes, reject with 404
|
||||
if (!matchedComponents.length) {
|
||||
return reject({ code: 404 })
|
||||
}*/
|
||||
|
||||
const appHtml = await renderer.renderToString(app)
|
||||
const appHtml = await renderToString(app)
|
||||
|
||||
const pageHtml = compiled({
|
||||
app: appHtml,
|
||||
...app.$meta().inject()
|
||||
})
|
||||
|
||||
resolve(pageHtml)
|
||||
})
|
||||
const pageHtml = compiled({
|
||||
app: appHtml,
|
||||
htmlAttrs: {
|
||||
text: () => {}
|
||||
},
|
||||
headAttrs: {
|
||||
text: () => {}
|
||||
},
|
||||
bodyAttrs: {
|
||||
text: () => {}
|
||||
},
|
||||
head: () => {},
|
||||
bodyPrepend: () => {},
|
||||
bodyAppend: () => {}
|
||||
//...app.$meta().inject()
|
||||
})
|
||||
|
||||
return pageHtml
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
<template>
|
||||
<h3>You're looking at the <strong>{{ page }}</strong> page</h3>
|
||||
<p>Has metaInfo been updated due to navigation? {{ metaUpdated }}</p>
|
||||
</template>
|
||||
|
||||
<head type="template">
|
||||
<title v-if="title">{{ title }}</title>
|
||||
<meta v-for="meta in metas" :name="meta.name" :content="meta.content" />
|
||||
</head>
|
||||
|
||||
<script>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
page: String
|
||||
},
|
||||
setup(props) {
|
||||
const metaUpdated = ref()
|
||||
const route = useRoute()
|
||||
|
||||
onMounted(() => console.log(route))
|
||||
|
||||
return { metaUpdated, page: route.value.name }
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
useMeta()
|
||||
+29
-25
@@ -1,16 +1,18 @@
|
||||
import Vue from 'vue'
|
||||
import { createApp, defineComponent, reactive, toRefs, h } from 'vue'
|
||||
import VueMeta from 'vue-meta'
|
||||
import Router from 'vue-router'
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import About from './about.vue'
|
||||
|
||||
Vue.use(Router)
|
||||
Vue.use(VueMeta, {
|
||||
/*Vue.use(VueMeta, {
|
||||
refreshOnceOnNavigation: true
|
||||
})
|
||||
})*/
|
||||
|
||||
let metaUpdated = 'no'
|
||||
const ChildComponent = {
|
||||
const ChildComponent = defineComponent({
|
||||
name: 'child-component',
|
||||
props: ['page'],
|
||||
props: {
|
||||
page: String
|
||||
},
|
||||
template: `<div>
|
||||
<h3>You're looking at the <strong>{{ page }}</strong> page</h3>
|
||||
<p>Has metaInfo been updated due to navigation? {{ metaUpdated }}</p>
|
||||
@@ -26,21 +28,25 @@ const ChildComponent = {
|
||||
}
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
setup () {
|
||||
const state = reactive({
|
||||
date: null,
|
||||
metaUpdated
|
||||
})
|
||||
|
||||
return {
|
||||
...toRefs(state)
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
/*mounted () {
|
||||
this.interval = setInterval(() => {
|
||||
this.date = new Date()
|
||||
}, 1000)
|
||||
},
|
||||
destroyed () {
|
||||
clearInterval(this.interval)
|
||||
}
|
||||
}
|
||||
}*/
|
||||
})
|
||||
|
||||
// this wrapper function is not a requirement for vue-router,
|
||||
// just a demonstration that render-function style components also work.
|
||||
@@ -48,25 +54,21 @@ const ChildComponent = {
|
||||
function view (page) {
|
||||
return {
|
||||
name: `section-${page}`,
|
||||
render (h) {
|
||||
return h(ChildComponent, {
|
||||
props: { page }
|
||||
})
|
||||
render () {
|
||||
return h(ChildComponent, { page })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const router = new Router({
|
||||
mode: 'history',
|
||||
base: '/vue-router',
|
||||
const router = createRouter({
|
||||
history: createWebHistory('/vue-router'),
|
||||
routes: [
|
||||
{ path: '/', component: view('home') },
|
||||
{ path: '/about', component: view('about') }
|
||||
{ name: 'home', path: '/', component: view('home') },
|
||||
{ name: 'about', path: '/about', component: About }
|
||||
]
|
||||
})
|
||||
|
||||
const App = {
|
||||
router,
|
||||
template: `
|
||||
<div id="app">
|
||||
<h1>vue-router</h1>
|
||||
@@ -80,8 +82,10 @@ const App = {
|
||||
`
|
||||
}
|
||||
|
||||
const app = new Vue(App)
|
||||
const app = createApp(App)
|
||||
app.use(router)
|
||||
|
||||
/*
|
||||
const { set, remove } = app.$meta().addApp('custom')
|
||||
|
||||
set({
|
||||
@@ -93,8 +97,8 @@ set({
|
||||
]
|
||||
})
|
||||
setTimeout(() => remove(), 3000)
|
||||
|
||||
app.$mount('#app')
|
||||
*/
|
||||
app.mount('#app')
|
||||
|
||||
/*
|
||||
const waitFor = time => new Promise(r => setTimeout(r, time || 1000))
|
||||
|
||||
@@ -2,7 +2,7 @@ import fs from 'fs'
|
||||
import path from 'path'
|
||||
import webpack from 'webpack'
|
||||
import WebpackBar from 'webpackbar'
|
||||
import VueLoaderPlugin from 'vue-loader/lib/plugin'
|
||||
import { VueLoaderPlugin } from 'vue-loader'
|
||||
|
||||
const srcDir = path.join(__dirname, '..', 'src')
|
||||
|
||||
@@ -15,7 +15,7 @@ export default {
|
||||
|
||||
if (dir === 'ssr') {
|
||||
entries[dir] = path.join(fullDir, 'browser.js')
|
||||
} else {
|
||||
} else if (dir === 'vue-router') {
|
||||
const entry = path.join(fullDir, 'app.js')
|
||||
if (fs.statSync(fullDir).isDirectory() && fs.existsSync(entry)) {
|
||||
entries[dir] = entry
|
||||
@@ -47,13 +47,24 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
{ test: /\.vue$/, use: 'vue-loader' }
|
||||
{
|
||||
resourceQuery: /blockType=head/,
|
||||
loader: require.resolve('./meta-loader.js')
|
||||
},
|
||||
{
|
||||
test: /\.vue$/,
|
||||
use: 'vue-loader'
|
||||
}
|
||||
]
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'vue': 'vue/dist/vue.js',
|
||||
'vue-meta': process.env.NODE_ENV === 'development' ? srcDir : 'vue-meta'
|
||||
// this isn't technically needed, since the default `vue` entry for bundlers
|
||||
// is a simple `export * from '@vue/runtime-dom`. However having this
|
||||
// extra re-export somehow causes webpack to always invalidate the module
|
||||
// on the first HMR update and causes the page to reload.
|
||||
'vue': 'vue/dist/vue.esm.js',
|
||||
'vue-meta': path.resolve(__dirname, './next/')
|
||||
}
|
||||
},
|
||||
// Expose __dirname to allow automatically setting basename.
|
||||
@@ -67,5 +78,12 @@ export default {
|
||||
new webpack.DefinePlugin({
|
||||
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development')
|
||||
})
|
||||
]
|
||||
],
|
||||
devServer: {
|
||||
inline: true,
|
||||
hot: true,
|
||||
stats: 'minimal',
|
||||
contentBase: __dirname,
|
||||
overlay: true
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user