Laravel 7 Blade模板中引入公共JS与页面专属JS时函数未定义问题排查
问题场景
我正在用Laravel 7开发应用,想要实现:
- 主布局里引入全局公共JS文件
common.js - 各个视图通过
@push('script')引入专属脚本或者直接写内联脚本
我的主布局body部分代码:
<body> <main> @yield('content') </main> <script src="{{ asset('js/common.js') }}"></script> @stack('script') </body>
其中一个视图的代码:
@section('content') <div class="game-version-list"> <ul> @foreach ($list as $element) <li> <a href="{{ route($element['name']) }}">{{ $element['text'] }}</a> <div> <span class="info-btn" data-key="{{ $element['key'] }}">?</span> <div class="game-info"></div> </div> </li> @endforeach </ul> </div> @endsection @push('script') <script> const btns = document.querySelectorAll('span'); btns.forEach(element => { element.addEventListener('click', getGameInfoMobile); }); </script> @endpush
遇到的问题
页面加载时提示getGameInfoMobile函数未定义,但这个函数明明已经在common.js里定义了。我试过在@stack里再次引入common.js,问题还是存在,只有把所有脚本都放进common.js才能正常运行。我不想把所有脚本都塞进app.js,确实需要这种分模块引入的方式,因为有大量函数要在多页面复用。
问题原因分析
你忽略了一个关键点:common.js中的函数是否被挂载到全局作用域。
如果你的common.js是用类似模块化方式编写的(比如用了闭包包裹或者ES模块的export语法),那么getGameInfoMobile函数只会在模块内部生效,不会成为全局window对象的属性,这样内联脚本就无法访问到它。
举个例子,以下写法都会导致函数无法被全局访问:
// 错误示例1:闭包包裹,函数未暴露到全局 (function() { function getGameInfoMobile() { // 函数逻辑 } })(); // 错误示例2:ES模块写法 export function getGameInfoMobile() { // 函数逻辑 }
解决方案
要让内联脚本能访问到common.js里的函数,你需要把函数暴露到全局作用域,有两种常用方式:
挂载到window对象
修改common.js,把需要全局访问的函数挂载到window上:function getGameInfoMobile() { // 你的函数逻辑 } // 暴露到全局作用域 window.getGameInfoMobile = getGameInfoMobile;确保脚本执行顺序与DOM加载完成同步
虽然布局里common.js在@stack之前加载,但如果common.js里的代码是异步执行或者依赖其他未加载的资源,也可能导致函数还没定义就被调用。这种情况下可以把内联脚本放在DOMContentLoaded事件里,确保DOM和全局脚本都加载完成后再执行:@push('script') <script> document.addEventListener('DOMContentLoaded', function() { const btns = document.querySelectorAll('span'); btns.forEach(element => { element.addEventListener('click', getGameInfoMobile); }); }); </script> @endpush
额外建议
如果你的项目后续会有更多复杂的JS逻辑,推荐使用Laravel Mix来管理JS模块,通过webpack.ProvidePlugin可以自动加载常用函数,既保持模块化,又能方便地在全局访问。
比如在webpack.mix.js里配置:
const mix = require('laravel-mix'); const webpack = require('webpack'); mix.js('resources/js/app.js', 'public/js') .webpackConfig({ plugins: [ new webpack.ProvidePlugin({ getGameInfoMobile: ['./common.js', 'getGameInfoMobile'] }) ] });
这样你就不需要手动挂载到window,Webpack会自动把函数注入到需要的地方。
内容的提问来源于stack exchange,提问作者Renato

