我应该如何在 Phoenix 1.5 的模板中包含 javascript

How should I include javascript in a template in Phoenix 1.5

我试图在我使用 webpack 的 phoenix 1.5 项目中包含一个使用 chart.js 的 javascript 文件。当我在模板中添加类似 <script>require('/js/graph').Graph.draw()</script> 的内容时,我在浏览器控制台中看到错误 Uncaught ReferenceError: require is not defined,并且未呈现图形。

package.json

{
  "repository": {},
  "license": "MIT",
  "scripts": {
    "deploy": "webpack --mode production",
    "watch": "webpack --mode development --watch"
  },
  "dependencies": {
    "chart.js": "^2.9.3",
    "phoenix": "file:../deps/phoenix",
    "phoenix_html": "file:../deps/phoenix_html"
  },
  "devDependencies": {
    "@babel/core": "^7.0.0",
    "@babel/preset-env": "^7.0.0",
    "babel-loader": "^8.0.0",
    "copy-webpack-plugin": "^4.5.0",
    "css-loader": "^2.1.1",
    "mini-css-extract-plugin": "^0.4.0",
    "optimize-css-assets-webpack-plugin": "^5.0.1",
    "terser-webpack-plugin": "^1.1.0",
    "webpack": "4.4.0",
    "webpack-cli": "^3.3.2"
  }
}

webpack.config.js

const path = require('path');
const glob = require('glob');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');

module.exports = (env, options) => ({
  optimization: {
    minimizer: [
      new TerserPlugin({ cache: true, parallel: true, sourceMap: false }),
      new OptimizeCSSAssetsPlugin({})
    ]
  },
  entry: {
    './js/app.js': glob.sync('./vendor/**/*.js').concat(['./js/app.js'])
  },
  output: {
    filename: 'app.js',
    path: path.resolve(__dirname, '../priv/static/js')
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader'
        }
      },
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader']
      }
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({ filename: '../css/app.css' }),
    new CopyWebpackPlugin([{ from: 'static/', to: '../' }])
  ]
});

graph.js

export const Graph = {
  draw() {
    const Chart = require('char.js');
    const ctx = document.getElementById('myChart');
    const myChart = new Chart(ctx, {
      type: 'bar',
      data: {
          labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
          datasets: [{
              label: '# of Votes',
              data: [12, 19, 3, 5, 2, 3],
              backgroundColor: [
                  'rgba(255, 99, 132, 0.2)',
                  'rgba(54, 162, 235, 0.2)',
                  'rgba(255, 206, 86, 0.2)',
                  'rgba(75, 192, 192, 0.2)',
                  'rgba(153, 102, 255, 0.2)',
                  'rgba(255, 159, 64, 0.2)'
              ],
              borderColor: [
                  'rgba(255, 99, 132, 1)',
                  'rgba(54, 162, 235, 1)',
                  'rgba(255, 206, 86, 1)',
                  'rgba(75, 192, 192, 1)',
                  'rgba(153, 102, 255, 1)',
                  'rgba(255, 159, 64, 1)'
              ],
              borderWidth: 1
          }]
      },
      options: {
          scales: {
              yAxes: [{
                  ticks: {
                      beginAtZero: true
                  }
              }]
          }
      }
    });
  }
}

index.html.eex

<div class="phx-hero">
  <h2>Graph: </h2>
  <canvas id="myChart" width="400" height="400"></canvas>
</div>
<script>require('/js/graph').Graph.draw()</script>

我有一个 phoenix 1.3 项目,这就是我在模板中使用 javascript 的方式,但它使用的是早午餐。我对 webpack 这样做正确吗?

让我们假设您希望它在所有页面中 运行。在这种情况下 "include" 你的 graph.js 在 app.js

// app.js
import {Graph} from 'graph';
Graph.draw();

但如果您只在特定页面需要它,请在 webpack.config.js

中创建新的 "entry"
// webpack.config.js
entry: {
  './js/app.js': glob.sync('./vendor/**/*.js').concat(['./js/app.js']),
  './js/mypage.js': 'js/mypage.js'
},

然后输入 mypage.js 与我在第一种情况下建议的代码相同,然后在您的页面中包含 mypage.js,如下所示:

<script src='<%= static_path("js/mypage.js") %>'></script>

应在布局中包含 app.js 文件后添加此行(我认为默认情况下它包含在那里)

编辑

我意识到 phoenix 默认 Webpack 具有硬编码输出,因此所有 js 代码仍将以相同的输出文件名出现,因此必须应用以下更改

入口点

  entry: {
    './js/app.js': glob.sync('./vendor/**/*.js').concat(['./js/app.js']),
    './js/graph.js': glob.sync('./vendor/**/*.js').concat(['./js/graph.js'])
  },

输出

请注意,'../priv/static/js' 已更改为 '../priv/static'filename: 'app.js' 已更改为 filename: '[name]'

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

插件

new CopyWebpackPlugin([{ from: 'static/', to: '../' }]) 改为 new CopyWebpackPlugin([{ from: 'static/', to: '' }])

  plugins: [
    new MiniCssExtractPlugin({ filename: 'css/app.css' }),
    new CopyWebpackPlugin([{ from: 'static/', to: '' }])
  ]

请注意,如果您之前进行了一些更改,您可能会略有不同,只需注意路径,因为 output.path 现在在文件夹结构中向上一级!