如何从服务器渲染的模板中将值传递给 Vue.js(即,将 Vue.js 应用程序与 django 模板集成) [英] How can values be passed to Vue.js from within a server-rendered template (i.e. integrate Vue.js app with a django template)

查看:39
本文介绍了如何从服务器渲染的模板中将值传递给 Vue.js(即,将 Vue.js 应用程序与 django 模板集成)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何将值从服务器渲染的模板中传递给 Vue.js(即,将 Vue.js 应用程序与 django 模板集成)?

用例

  • 增强服务器渲染环境中的特定页面:在服务器渲染的 Web 应用程序(即 django)的一个特定页面上嵌入一个简单的 vue 应用程序,以增强特定功能(即注册表单)的前端用户体验
  • 内容管理系统 (CMS):在服务器渲染的 CMS 中,vue 应用程序可以作为独立的 CMS 组件向编辑器公开.然后,编辑器可以选择将 vue 应用程序放置在特定页面上的特定位置.

集成挑战:

  1. 全局设置,例如端点 URL 或其他此类环境变量
  2. 编辑器设置的配置:即用户个人资料图片的 URL(当然最好通过 REST API 获取此类信息).
  3. translations:如果您的服务器渲染的 Web 框架已经集中处理翻译(即 django 的 makemessages CLI 命令),那么最好包含 Vue.js 应用程序字符串翻译.

解决方案

这里是 django + Vue.js v2 的第一个解决方案.它包括一些仅与 django CMS 相关的内容.但一般原则适用于任何服务器端渲染 CMS 或 Web 框架.

在您的 Webpack 配置中启用 Vue.js 支持,如下所示:

frontend/webpack.config.js:

'use strict';const path = require('path');const MiniCssExtractPlugin = require(`mini-css-extract-plugin`);const VueLoaderPlugin = require('vue-loader/lib/plugin');const isServerMode = process.env.NODE_ENV === '生产';常量配置 = {模式:'发展',入口: {//在这里,可以定义任意数量的入口点(包)"marketplace-app": './frontend/blocks/marketplace-app/index.js',},输出: {文件名:'[名称].bundle.js',路径:__dirname + '/dist/',publicPath: `http://localhost:8090/assets/`,},模块: {规则: [{测试:/\.tsx?$/,使用:'ts-loader',排除:/node_modules/,},{测试:/\.svg$/i,排除:/字体/,loader: 'svg-url-loader',},{测试:/\.(sass|scss|css)$/,用: [{加载器:MiniCssExtractPlugin.loader,选项: {源地图:真实,插件:() =>{返回 [要求('precss'),要求('自动前缀'),];},hmr:是的,}},{loader: 'css-loader', 选项: {sourceMap: true}},{loader: 'sass-loader', 选项: {sourceMap: true}},]},{//图片测试:/\.(jpe?g|png|gif)$/i,用: [{加载器:'文件加载器',选项: {询问: {哈希:'sha512',摘要:'十六进制',name: '[name].[ext]'}}},{loader: 'image-webpack-loader',选项: {询问: {绕过调试:'真',mozjpeg: {progressive: true},gifsicle:{隔行:真实},optipng: {optimizationLevel: 7},}}}]},{测试:/\.(svg)(\?[\s\S]+)?$/,//svg 字体不能像上面处理 svg 图像那样处理//因此它们在这里被分开处理包括:/字体/,用: ['文件加载器']},{测试:/\.woff2?(\?v=[0-9]\.[0-9]\.[0-9])?$/,加载器:'文件加载器',},{测试:/\.(ttf|eot)(\?[\s\S]+)?$/,加载器:'文件加载器',},{测试:/\.modernizrrc.js$/,使用:['modernizr-loader'],},{测试:/\.modernizrrc(\.json)?$/,使用: ['modernizr-loader', 'json-loader'],},{测试:/\.vue$/,用: [{loader: 'vue-loader'},/*{loader: 'eslint-loader'//如果你想在 linting 失败时编译失败,你可以取消注释}*/]},],},解决: {扩展名:['.ts', '.tsx', '.js', '.vue'],模块: [path.resolve('前端'),'节点模块'],别名:{Modernizr$: path.resolve(__dirname, '/frontend/.modernizrrc'),Vue:process.env.NODE_ENV === '生产' ?'vue/dist/vue.min.js' : 'vue/dist/vue.js',}},开发服务器:{contentBase: path.resolve(__dirname, `frontend`),标头:{'Access-Control-Allow-Origin':'*'},主机:`本地主机`,端口:8090,热:真的,内联:真实,},插件: [新的 VueLoaderPlugin(),新的 MiniCssExtractPlugin({filename: '[name].css'}),],开发工具:'评估源地图',优化: {//webpack 文档中的默认配置,大部分可能没用splitChunks:{块:'异步',最小尺寸:30000,最大尺寸:0,minChunks: 1,最大异步请求数:5,最大初始请求数:3,自动名称分隔符: '~',名称:真实,缓存组:{供应商:{测试:/[\\/]node_modules[\\/]/,优先级:-10,},默认: {minChunks: 2,优先级:-20,重用ExistingChunk:真,},},},},};如果(isServerMode){config.mode = '生产';config.devtool = '无';config.output.filename = '[名称].js';config.output.publicPath = '/static/dist/';}module.exports = config;

服务器端模板的示例(即在 django 中,这可能类似于 backend/marketplace/plugins/marketplace_list_plugin/templates/marketplace_list_plugin/marketplace-list-plugin.html):

注意事项:

  • 此演示了如何将一个值(一个端点 URL)从 django 模板导入到 Vue.js 应用程序中.
  • 基于全局设置变量settings.DJANGO_ENV
  • 支持本地开发环境模式,通过webpack开发服务器热重载
  • settings.WEBPACK_DEV_URL 需要从 webpack.config.js
  • 中的 Webpack 开发服务器设置中设置为 publicPath
  • data-is-reload-on-page-edit 类需要绑定到在前端编辑操作时重新加载 js 源的 CMS 功能(如果 CMS 具有此类功能).

{% 加载 i18n staticfiles 静态缩略图 %}{% comment %} 这是一个来自 frontend/blocks/marketplace-app/index.js {% endcomment %} 的 vue 小部件<div id="{{ instance.anchor_id }}"><div class="marketplace-vue-widget" data-endpoint-url='{% url 'marketplace:article-list' %}'></div>

{% if settings.DJANGO_ENV.value == 'local' %}<script data-is-reload-on-page-edit defer src="{{ settings.WEBPACK_DEV_URL }}/marketplace-app.bundle.js"></script><link rel="stylesheet" href="{{ settings.WEBPACK_DEV_URL }}/marketplace-app.css">{% 别的 %}<script data-is-reload-on-page-edit defer src="{% static 'dist/marketplace-app.js' %}"></script><link rel="stylesheet" href="{% static 'dist/marketplace-app.css' %}">{% 万一 %}

前端设置(frontend/blocks/marketplace-app/index.js):

从'vue'导入Vue;从'./app.vue'导入应用程序//这与 backend/marketplace/plugins/marketplace_list_plugin/templates/marketplace_list_plugin/marketplace-list-plugin.html 有关//CMS 插件(理论上)可以多次添加到页面中,//我们注意允许 vue 实例被挂载在任何事件上//为你的小部件创建一个构造函数让小部件 = Vue.extend({渲染(h){返回 h(App, {props: {//从 django 模板中的数据属性加载内容endpointUrl: this.$el.getAttribute('data-endpoint-url'),}})},});//将小部件挂载到任何出现处let nodes = document.querySelectorAll('.marketplace-vue-widget');//唯一选择器!for (let i = 0; i 

app.vue:

<script src="./app.js"></script><style lang="scss" 作用域 src="./app.scss"></style>

app.js:

//示例代码导出默认{道具:[端点网址"],数据() {返回 {}},计算:{},安装(){},手表: {},方法: {},成分: {},}

How can values be passed to Vue.js from within a server-rendered template (i.e. integrate Vue.js app with a django template)?

Use Cases

  • Enhancement of specific pages in a server-rendered environment: Embed a simple vue application on one specific page of your server-rendered web application (i.e. django) to enhance the frontend user experience for a specific functionality (i.e. a signup form)
  • Content Management Systems (CMS): In a server-rendered CMS, a vue application can be exposed to the editors as an independent CMS component. The editor can then choose where to place the vue application on specific pages, at specific positions.

Integration Challenges:

  1. Global settings, such as endpoint URLs or other such environment variables
  2. configurations set by the editor: i.e. URL of user profile picture (of course it would be better to fetch such information via a REST API).
  3. translations: If your server-rendered web framework already centrally handles translations (i.e. django's makemessages CLI command) it would be great to include the Vue.js application string translations.

解决方案

Here is a first solution approach for django + Vue.js v2. It includes some stuff that is only relevant for django CMS. But the general principle applies to any server-side rendering CMS or web framework.

Enable Vue.js support in your Webpack config, something like the below:

frontend/webpack.config.js:

'use strict';


const path = require('path');
const MiniCssExtractPlugin = require(`mini-css-extract-plugin`);
const VueLoaderPlugin = require('vue-loader/lib/plugin');

const isServerMode = process.env.NODE_ENV === 'production';


const config = {
    mode: 'development',
    entry: {
        // here, any number of entrypoints (bundles) can be defined
        "marketplace-app": './frontend/blocks/marketplace-app/index.js',
    },
    output: {
        filename: '[name].bundle.js',
        path: __dirname + '/dist/',
        publicPath: `http://localhost:8090/assets/`,
    },
    module: {
        rules: [
            {
                test: /\.tsx?$/,
                use: 'ts-loader',
                exclude: /node_modules/,
            },
            {
                test: /\.svg$/i,
                exclude: /fonts/,
                loader: 'svg-url-loader',
            },
            {
                test: /\.(sass|scss|css)$/,
                use: [
                    {
                        loader: MiniCssExtractPlugin.loader,
                        options: {
                            sourceMap: true,
                            plugins: () => {
                                return [
                                    require('precss'),
                                    require('autoprefixer'),
                                ];
                            },
                            hmr: true,
                        }
                    },
                    {loader: 'css-loader', options: {sourceMap: true}},
                    {loader: 'sass-loader', options: {sourceMap: true}},
                ]
            },
            {
                // images
                test: /\.(jpe?g|png|gif)$/i,
                use: [
                    {
                        loader: 'file-loader',
                        options: {
                            query: {
                                hash: 'sha512',
                                digest: 'hex',
                                name: '[name].[ext]'
                            }
                        }
                    },
                    {
                        loader: 'image-webpack-loader',
                        options: {
                            query: {
                                bypassOnDebug: 'true',
                                mozjpeg: {progressive: true},
                                gifsicle: {interlaced: true},
                                optipng: {optimizationLevel: 7},
                            }
                        }
                    }
                ]
            },
            {
                test: /\.(svg)(\?[\s\S]+)?$/,
                // svg fonts cannot be processed the way we do with svg images above
                // therefore they are handled separately here
                include: /fonts/,
                use: [
                    'file-loader'
                ]
            },
            {
                test: /\.woff2?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
                loader: 'file-loader',
            },
            {
                test: /\.(ttf|eot)(\?[\s\S]+)?$/,
                loader: 'file-loader',
            },
            {
                test: /\.modernizrrc.js$/,
                use: ['modernizr-loader'],
            },
            {
                test: /\.modernizrrc(\.json)?$/,
                use: ['modernizr-loader', 'json-loader'],
            },
            {
                test: /\.vue$/,
                use: [{
                    loader: 'vue-loader'
                }, /*{
                    loader: 'eslint-loader' // You can uncomment this if you want compiling to fail if linting fails
                }*/]
            },
        ],
    },
    resolve: {
        extensions: ['.ts', '.tsx', '.js', '.vue'],
        modules: [
            path.resolve('frontend'),
            'node_modules'
        ],
        alias: {
            modernizr$: path.resolve(__dirname, '/frontend/.modernizrrc'),
            vue: process.env.NODE_ENV === 'production' ? 'vue/dist/vue.min.js' : 'vue/dist/vue.js',
        }
    },
    devServer: {
        contentBase: path.resolve(__dirname, `frontend`),
        headers: {'Access-Control-Allow-Origin': '*'},
        host: `localhost`,
        port: 8090,
        hot: true,
        inline: true,
    },
    plugins: [
        new VueLoaderPlugin(),
        new MiniCssExtractPlugin({filename: '[name].css'}),
    ],
    devtool: 'eval-source-map',
    optimization: {
        // the default config from webpack docs, most of it might be useless
        splitChunks: {
            chunks: 'async',
            minSize: 30000,
            maxSize: 0,
            minChunks: 1,
            maxAsyncRequests: 5,
            maxInitialRequests: 3,
            automaticNameDelimiter: '~',
            name: true,
            cacheGroups: {
                vendors: {
                    test: /[\\/]node_modules[\\/]/,
                    priority: -10,
                },
                default: {
                    minChunks: 2,
                    priority: -20,
                    reuseExistingChunk: true,
                },
            },
        },
    },
};


if (isServerMode) {
    config.mode = 'production';
    config.devtool = 'none';
    config.output.filename = '[name].js';
    config.output.publicPath = '/static/dist/';
}


module.exports = config;

example for the server-side template (i.e. in django this could be something like backend/marketplace/plugins/marketplace_list_plugin/templates/marketplace_list_plugin/marketplace-list-plugin.html):

Notes:

  • This demos how to import one value (an endpoint url) from the django template into the Vue.js application.
  • supports local dev env mode with hot reloading via webpack dev server based on the value of a global setting variable settings.DJANGO_ENV
  • settings.WEBPACK_DEV_URL needs to be set to publicPath from the Webpack dev server setting in webpack.config.js
  • data-is-reload-on-page-edit class needs to be tied to CMS functionality that reloads the js sources upon a frontend editing action, if CMS has such functionality.

{% load i18n staticfiles static thumbnail %}

{% comment %}   this is a vue widget from frontend/blocks/marketplace-app/index.js   {% endcomment %}

<div id="{{ instance.anchor_id }}">
    <div class="marketplace-vue-widget" data-endpoint-url='{% url 'marketplace:article-list' %}'></div>
</div>

{% if settings.DJANGO_ENV.value == 'local' %}
    <script data-is-reload-on-page-edit defer src="{{ settings.WEBPACK_DEV_URL }}/marketplace-app.bundle.js"></script>
    <link rel="stylesheet" href="{{ settings.WEBPACK_DEV_URL }}/marketplace-app.css">
{% else %}
    <script data-is-reload-on-page-edit defer src="{% static 'dist/marketplace-app.js' %}"></script>
    <link rel="stylesheet" href="{% static 'dist/marketplace-app.css' %}">
{% endif %}

Frontend setup (frontend/blocks/marketplace-app/index.js):

import Vue from 'vue';
import App from './app.vue'


// this relates to backend/marketplace/plugins/marketplace_list_plugin/templates/marketplace_list_plugin/marketplace-list-plugin.html
// CMS plugins could (in theory) be added more than once to a page,
// we take care to allow the vue instance to be mounted on any occurrences

// create a constructor for your widget
let Widget = Vue.extend({
    render(h) {
        return h(App, {
            props: { // load stuff from data attributes in the django template
                endpointUrl: this.$el.getAttribute('data-endpoint-url'),
            }
        })
    },
});

// mount the widget to any occurences
let nodes = document.querySelectorAll('.marketplace-vue-widget'); // unique selector!
for (let i = 0; i < nodes.length; ++i) {
    new Widget({el: nodes[i]})
}

app.vue:

<template src='./app.html'></template>
<script src="./app.js"></script>
<style lang="scss" scoped src="./app.scss"></style>

app.js:

// example code

export default {
    props:["endpointUrl"],
    data() {
        return {}
    },
    computed: {},
    mounted() {},
    watch: {},
    methods: {},
    components: {},
}

这篇关于如何从服务器渲染的模板中将值传递给 Vue.js(即,将 Vue.js 应用程序与 django 模板集成)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆