WordPress定制器和webpack - 暴露wp全局

时间:2018-05-05 14:14:41

标签: javascript php wordpress webpack

我使用webpack捆绑我的文件。我想添加一个脚本,使我能够动态更改自定义程序中的设置。

我创建了一个包含所有自定义程序功能的类

<?php
/**
 * The Customizer specific functionality.
 *
 * @since   1.0.0
 * @package mytheme
 */

namespace MyTheme\Customizer;

use MyTheme\Helpers as Helpers;

/**
 * Class Customizer
 */
class Customizer {

  /**
   * Global theme name
   *
   * @var string
   *
   * @since 1.0.0
   */
  protected $theme_name;

  /**
   * Global theme version
   *
   * @var string
   *
   * @since 1.0.0
   */
  protected $theme_version;

  /**
   * Initialize class
   *
   * @param array                  $theme_info Load global theme info.
   * @param Helpers\General_Helper $helpers Instance of the General Helpers object.
   *
   * @since 1.0.0
   */
  public function __construct( $theme_info = null, Helpers\General_Helper $helpers ) {
    $this->theme_name    = $theme_info['theme_name'];
    $this->theme_version = $theme_info['theme_version'];

    $this->helpers = $helpers;

  }

  /**
   * Register customizer settings
   *
   * @see add_action('customize_register',$func)
   * @param  \WP_Customize_Manager $wp_customize WP Customize object.
   * @since 1.0.0
   */
  public function register_customizer_settings( \WP_Customize_Manager $wp_customize ) {
    // Abort if selective refresh is not available.
    if ( ! isset( $wp_customize->selective_refresh ) ) {
      return;
    }

    /**
     * Footer section
     */
    $wp_customize->add_section( 'footer_section', array(
        'title'    => esc_html__( 'Footer', 'mytheme' ),
        'priority' => 100,
    ) );

    /**
     * Copyright notice
     */
    $wp_customize->add_setting( 'footer_copyright', array(
        'default'           => '',
        'type'              => 'theme_mod',
        'transport'         => 'postMessage',
        'sanitize_callback' => 'wp_kses_post',
    ) );

    $wp_customize->add_control( 'footer_copyright', array(
        'label'       => esc_html__( 'Footer Copyright Text', 'mytheme' ),
        'section'     => 'footer_section',
        'type'        => 'text',
    ) );
  }

  /**
   * Enqueue live preview script
   *
   * @since 1.0.0
   */
  public function live_preview_enqueue() {
    $customizer_script = '/skin/public/scripts/customizer.js';

    wp_register_script( $this->theme_name . '-customizer', get_template_directory_uri() . $customizer_script, array(), $this->helpers->get_assets_version( $customizer_script ) );
    wp_enqueue_script( $this->theme_name . '-customizer', array( 'jquery', 'customize-preview' ), false );
  }
}

在另一个我加载动作的课程中,我有

private function define_customizer_hooks() {
  $customizer = new Customizer\Customizer( $this->get_theme_info(), new Helpers\General_Helper() );

  $this->loader->add_action( 'customize_register', $customizer, 'register_customizer_settings', 11 );
  $this->loader->add_action( 'customize_preview_init', $customizer, 'live_preview_enqueue' );
}

现在,我可以看到我的设置很好。 Footer section正在显示。但是我的控制台出错了

  

jQuery.Deferred异常:wp.customize不是函数TypeError:wp.customize不是函数

customizer.js 看起来像这样

/* global wp */
$(function() {
  wp.customize('footer_copyright', (value) => {
    value.bind((newval) => {
      if ($('.footer__copyright').length) {
        if (newval !== '') {
          if ($('.footer__copyright').hasClass('hide')) {
            $('.footer__copyright').removeClass('hide').text(newval);
          } else {
            $('.footer__copyright').text(newval);
          }
        } else {
          $('.footer__copyright').addClass('hide');
        }
      } else {
        $('.footer__container').append(`<div class="footer__copyright">${newval}</div>`);
      }
    });
  });
});

我的 webpack.config.js 看起来像这样:

/* global process __dirname */

const DEV = process.env.NODE_ENV !== 'production';

const path = require('path');
const webpack = require('webpack');

const CleanWebpackPlugin = require('clean-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const BrowserSyncPlugin = require('browser-sync-webpack-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');

const appPath = `${path.resolve(__dirname)}`;

// Dev Server
const proxyUrl = 'mytheme.test';

// Theme
const themeName = 'mytheme';
const themePath = `/wp-content/themes/${themeName}/skin`;
const themeFullPath = `${appPath}${themePath}`;
const themePublicPath = `${themePath}/public/`;
const themeEntry = `${themeFullPath}/assets/application.js`;
const themeAdminEntry = `${themeFullPath}/assets/application-admin.js`;
const themeGutenbergEntry = `${themeFullPath}/assets/application-gutenberg.js`;
const themeCustomizerEntry = `${themeFullPath}/assets/customizer.js`;
const themeOutput = `${themeFullPath}/public`;

// Outputs
const outputJs = 'scripts/[name].js';
const outputCss = 'styles/[name].css';
const outputFile = '[name].[ext]';
const outputImages = `images/${outputFile}`;
const outputFonts = `fonts/${outputFile}`;

const allModules = {
  rules: [
    {
      test: /\.(js|jsx)$/,
      use: 'babel-loader',
      exclude: /node_modules/,
    },
    {
      test: /\.json$/,
      exclude: /node_modules/,
      use: 'file-loader',
    },
    {
      test: /\.(png|svg|jpg|jpeg|gif|ico)$/,
      exclude: [/fonts/, /node_modules/],
      use: `file-loader?name=${outputImages}`,
    },
    {
      test: /\.(eot|otf|ttf|woff|woff2|svg)$/,
      exclude: [/images/, /node_modules/],
      use: `file-loader?name=${outputFonts}`,
    },
    {
      test: /\.scss$/,
      exclude: /node_modules/,
      use: [
        MiniCssExtractPlugin.loader,
        'css-loader', 'postcss-loader', 'sass-loader',
      ],
    },
  ],
};

const allPlugins = [
  new MiniCssExtractPlugin({
    filename: outputCss,
  }),

  new webpack.ProvidePlugin({
    $: 'jquery',
    jQuery: 'jquery',
  }),

  // Use BrowserSync For assets
  new BrowserSyncPlugin({
    host: 'localhost',
    port: 3000,
    proxy: proxyUrl,
    files: [
      {
        match: ['wp-content/themes/**/*.php', 'wp-content/plugins/**/*.php'],
      },
    ],
  }),

  new webpack.DefinePlugin({
    'process.env': {
      NODE_ENV: JSON.stringify(process.env.NODE_ENV || 'development'),
    },
  }),

  new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
];

const allOptimizations = {
  runtimeChunk: false,
  splitChunks: {
    cacheGroups: {
      commons: {
        test: /[\\/]node_modules[\\/]/,
        name: 'vendors',
        chunks: 'all',
      },
    },
  },
};

// Use only for production build
if (!DEV) {
  allOptimizations.minimizer = [
    new UglifyJsPlugin({
      cache: true,
      parallel: true,
      sourceMap: true,
      uglifyOptions: {
        output: {
          comments: false,
        },
        compress: {
          warnings: false,
          drop_console: true, // eslint-disable-line camelcase
        },
      },
    }),
  ];
  allPlugins.push(new CleanWebpackPlugin([themeOutput]));
}

module.exports = [

  // Theme Skin
  {
    context: path.join(__dirname),
    entry: {
      application: [themeEntry],
      applicationAdmin: [themeAdminEntry],
      themeGutenberg: [themeGutenbergEntry],
      customizer: [themeCustomizerEntry],
    },
    output: {
      path: themeOutput,
      publicPath: themePublicPath,
      filename: outputJs,
      library: ['wp', '[name]'],
      libraryTarget: 'var',
    },

    externals: {
      wp: 'wp',
    },

    optimization: allOptimizations,

    mode: 'production',

    module: allModules,

    plugins: allPlugins,

    devtool: DEV ? '#inline-source-map' : '',
  },
];

package.json

{
  "name": "mytheme",
  "version": "1.0.0",
  "author": "dingo_d",
  "private": true,
  "main": "",
  "scripts": {
    "__sassUnusedTheme": "sass-unused \"wp-content/themes/init_theme_name/**/*.scss\"",
    "__eslintTheme": "eslint wp-content/themes/init_theme_name/skin/assets/",
    "__stylelintTheme": "stylelint \"wp-content/themes/init_theme_name/**/*.scss\" --syntax scss",
    "precommitCss": "npm run __stylelintTheme",
    "precommitCssUnused": "npm run __sassUnusedTheme",
    "precommitJs": "npm run __eslintTheme",
    "precommit": "npm run precommitJs && npm run precommitCss && npm run precommitCssUnused",
    "start": "webpack --progress --watch --display-error-details --display-reasons",
    "build": "NODE_ENV=production webpack --progress"
  },
  "devDependencies": {
    "@infinumjs/eslint-config": "^1.1.0",
    "@infinumjs/stylelint-config": "^1.0.0",
    "babel-core": "^6.26.3",
    "babel-eslint": "^8.2.3",
    "babel-loader": "^7.1.4",
    "babel-plugin-syntax-dynamic-import": "^6.18.0",
    "babel-preset-env": "^1.6.1",
    "browser-sync": "^2.24.1",
    "browser-sync-webpack-plugin": "^2.2.2",
    "clean-webpack-plugin": "^0.1.19",
    "copy-webpack-plugin": "^4.5.1",
    "css-loader": "^0.28.11",
    "eslint": "^4.19.1",
    "eslint-plugin-import": "^2.11.0",
    "expose-loader": "^0.7.5",
    "extract-text-webpack-plugin": "^3.0.2",
    "file-loader": "^1.1.11",
    "imports-loader": "^0.8.0",
    "mini-css-extract-plugin": "^0.4.0",
    "node-sass": "^4.9.0",
    "postcss-cssnext": "^3.1.0",
    "postcss-loader": "^2.1.4",
    "precss": "^3.1.2",
    "sass-loader": "^7.0.1",
    "style-loader": "^0.21.0",
    "stylelint": "^9.2.0",
    "uglifyjs-webpack-plugin": "^1.2.5",
    "webpack": "^4.6.0",
    "webpack-cli": "^2.1.2"
  },
  "dependencies": {
    "autoprefixer": "^8.4.1",
    "babel-polyfill": "^6.26.0",
    "bugsnag-js": "^4.6.0",
    "css-mqpacker": "^6.0.2",
    "cssnano": "^3.10.0",
    "jquery": "^3.3.1",
    "jquery-match-height": "^0.7.2",
    "layzr.js": "^2.2.2",
    "media-blender": "^2.1.0",
    "normalize.css": "^8.0.0",
    "npm": "^6.0.0",
    "postcss-font-magician": "^2.1.1",
    "slick-carousel": "^1.8.1",
    "whatwg-fetch": "^2.0.4"
  },
  "browserslist": [
    "android >= 4.2",
    "not ie < 11",
    "last 2 versions",
    "Safari >= 8"
  ]
}

困扰我的是我基本上使用(和修改)了我的代码,该代码在没有所有花哨的构建工具(https://themes.svn.wordpress.org/expire/1.0.9/inc/customizer/js/customizer.js)的情况下构建的主题中工作

我试过

/* global wp */
(function(api) {
  api('footer_copyright', (value) => {
    value.bind((newval) => {
      if ($('.footer__copyright').length) {
        if (newval !== '') {
          if ($('.footer__copyright').hasClass('hide')) {
            $('.footer__copyright').removeClass('hide').text(newval);
          } else {
            $('.footer__copyright').text(newval);
          }
        } else {
          $('.footer__copyright').addClass('hide');
        }
      } else {
        $('.footer__container').append(`<div class="footer__copyright">${newval}</div>`);
      }
    });
  });
})(wp.customize);

但是,我得到了

  

未捕获的ReferenceError:未定义wp

在检查器中,可能在加载完所有内容后wp.customize可用(ƒ (){return f.instance.apply(f,arguments)})。

我遵循了使用网络包的Gutenberg小组给出的建议,但我不确定我是否做得对。

3 个答案:

答案 0 :(得分:1)

Not sure if this is the best solution but I decided to add a dependency in my wp_enqueue_script call. For example I initially had:

wp_enqueue_script('scripts/admin/remove_category.js', asset_path('scripts/admin/remove_category.js'));

But then I had a search through the WordPress JS files to see which ones used wp, I figured I could pick an arbitrary one of these to use as a dependency and subsequently ensure wp is available inside my script, like this:

wp_enqueue_script('scripts/admin/remove_category.js', asset_path('scripts/admin/remove_category.js'), ['wp-util']);

答案 1 :(得分:0)

wp.customize对象已在全局范围内可用。来自WordPress&#39; JavaScript Reference/wp

  

window.wp对象用于命名多个有用的Javascript   WordPress管理员的类对象。

window.wp.customize更改为class Expense extends Model { protected $dates = ['date', 'another_date_field']; //other stuff } 应该可以解决问题。

答案 2 :(得分:0)

定制器有一些用于排队脚本的有用操作:

  • 如果您在 JS 代码中挂钩以影响定制器中的控件,那么您需要使用 customize_controls_enqueue_scripts
  • 如果您将 JS 代码挂钩到预览框架中,并且可能 通过 postMessage 从控件接收事件,从而能够 快速动态更改预览框的内容,您 将使用 customize_preview_init

所以我认为在您的情况下,您应该使用 customize_controls_enqueue_scripts 将脚本与 wp_enqueue_script 函数排入队列。

@see https://wordpress.stackexchange.com/a/138646/90804